前言
长链剖分是很早以前就听\(hl666\)神仙说过的算法,好像在处理与树上深度有关的问题时非常有用,而且还可以用于优化树形\(DP\)。
现在为了肝希望而下定决心去学一学。
什么是长链剖分?
其概念可以参考树链剖分(重链剖分)。
根据重链剖分的定义,重节点表示\(Size\)最大的子节点。
而我们的长链剖分中,长节点表示到叶节点链最长的子节点。
其余部分的定义是与树剖相类似的,因此没什么好讲。
典型应用\(1\):\(O(1)\)求\(k\)级祖先
下面,我们借助一个典例,来进一步了解一下长链剖分,即如何通过\(O(logn)\)的预处理,来\(O(1)\)求出\(k\)级祖先。
首先我们要知道一个性质:任意一个点\(k\)级祖先所在长链长度大于等于\(k\)。
证明:假设长度小于\(k\),则从该祖先到该节点的链的长度就大于这条长链的长度,与长链的定义不符,故假设不成立,原命题得证。
接下来,我们一开始先通过倍增,求出\(fa_{i,j}\)表示\(i\)的\(2^j\)级祖先。
而与此同时,对于每条长链的顶点,我们依次分别用两个\(vector\ u\)和\(d\)存下其祖先节点和长链上的所有节点。
然后,求答案时,设\(k\)的最高位为第\(hb\)位(\(HighestBit\)),则我们先找到\(x\)的\(k\ xor\ 2^{hb}\)级祖先\(f\),并设\(top_f\)为\(f\)所在长链的顶点。
下一步,我们比较\(dep_f-dep_{top_f}\)与\(k\ xor\ 2^{hb}\),如果\(dep_f-dep_{top_f}\ge k\ xor\ 2^{hb}\),说明最终答案在这条长链上,否则说明是顶点的祖先。
如果在这条长链上,返回\(d_{top_f,(dep_f-dep_{top_f})-(k\ xor\ 2^{hb})}\),否则返回\(u_{top_f,(k\ xor\ 2^{hb})-(dep_f-dep_{top_f})}\)。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
#define Log 20
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
#undef D
}F;
class LongChainDissection//长链剖分
{
private:
int fa[N+5][Log+5],son[N+5],dep[N+5],len[N+5],top[N+5],hb[N+5];
vector<int> u[N+5],d[N+5];
I void dfs1(CI x)//第一遍搜索
{
RI i;for(i=1;i<=Log;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
for(i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x][0]&&
(
dep[e[i].to]=dep[fa[e[i].to][0]=x]+1,
dfs1(e[i].to),len[e[i].to]>len[son[x]]&&(son[x]=e[i].to)
);len[x]=len[son[x]]+1;
}
I void dfs2(CI x,RI t)//第二遍搜索
{
RI i;for(top[x]=(t?t:t=x),i=lnk[x];i;i=e[i].nxt)
e[i].to^fa[x][0]&&(dfs2(e[i].to,e[i].to^son[x]?0:t),0);
if(x^t) return;
for(i=x;i;i=fa[i][0]) u[x].push_back(i);//记录祖先
for(i=x;i;i=son[i]) d[x].push_back(i);//记录长链上节点
}
public:
I void Init() {dfs1(1),dfs2(1,0);for(RI i=1,t=-1;i<=n;++i) (i>>t+1)&1&&++t,hb[i]=t;}//初始化
I int Anc(RI x,RI y)//求x的y级祖先
{
if(y>dep[x]) return 0;if(!y) return x;if(x=fa[x][hb[y]],!(y^=1<<hb[y])) return x;//特判几种情况
RI t=dep[x]-dep[top[x]];return t>=y?d[top[x]][t-y]:u[top[x]][y-t];//判断是否在链上,从而确定对应答案
}
}D;
int main()
{
RI Qtot,i,x,y;for(F.read(n),i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);//建边
D.Init(),F.read(Qtot);W(Qtot--) F.read(x,y),F.writeln(D.Anc(x,y));//处理询问
return F.clear(),0;
}
典型应用\(2\):优化\(DP\)
下面给出一道长链剖分优化\(DP\)的例题:【CF1009F】Dominant Indices。
标签:长链,剖分,dep,top,笔记,hb,define 来源: https://www.cnblogs.com/chenxiaoran666/p/LongChainDissection.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。