树链剖分讲解&模板
Posted fang-hao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分讲解&模板相关的知识,希望对你有一定的参考价值。
原理&实现
线段树是一种可以快速进行区间修改和查询的数据结构,并且我们已经知道可以通过dfs序加线段树来维护一棵子树的信息,那么,有没有一种方法来维护树上的两个点之间的链的信息的方法呢?当然是有的,这时就要请出树链剖分了。
对于一棵有根树,我们维护两个节点之间的路径信息时,可以想到维护两个点到lca的分别的信息,既然是要维护一条链,那么我们尽可能把树的节点进行重新编号,使其有更多连续的编号的节点形成链,这样我们更容易在线段树里去维护它。
所以,我们需要把树上的链分为重链和轻链,重链是由一系列重儿子相连而成的链,所谓重儿子,就是一个节点的儿子中子树大小最大的儿子,这样可以控制不超过logn条链(证明可以自己想想,并不难想),而我们在线段树里维护的复杂度为logn,所以树链剖分的时间复杂度为O(lognlogn)。
下面我们来讲讲具体实现:
fa 父亲节点的编号
top 所在重链的顶端节点的编号
hv 重儿子的编号
sz 子树大小
dep 当前节点深度
w 当前节点在树链剖分中的新编号
首先,我们需要用两个dfs处理出以上信息,我们用第一个dfs处理出fa,hv,sz,dep这几个信息,我们用第二个dfs求出w,top,顺便把所有的节点信息插入线段树
操作时,我们只需要顺着两个节点一直向上找到lca,所以我们可以利用之前处理出的top信息,不断将top的深度更大的节点向top跳,在跳的过程中修改或者查询,注意相遇时的特判!
模板代码
(太丑了不要嫌弃qwq)
1 #include<bits/stdc++.h> 2 #define maxn 100005 3 using namespace std; 4 int n,m,r,p,cnt=0,an=0,num[maxn],fa[maxn],top[maxn],hv[maxn],sz[maxn],w[maxn],dep[maxn],sum[maxn*7],lazy[maxn*7]; 5 vector<int>e[maxn]; 6 inline void read(int &x){ 7 x=0; register char ch=getchar(); 8 while(ch<‘0‘||ch>‘9‘)ch=getchar(); 9 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 10 } 11 inline void push(int ID,int nl,int nr){ 12 int m=(nl+nr)>>1; 13 sum[ID<<1]+=lazy[ID]*(m-nl+1),sum[ID<<1|1]+=lazy[ID]*(nr-m); 14 lazy[ID<<1]+=lazy[ID],lazy[ID<<1|1]+=lazy[ID],lazy[ID]=0; 15 sum[ID<<1]%=p,sum[ID<<1|1]%=p,lazy[ID<<1]%=p,lazy[ID<<1|1]%=p; 16 } 17 inline void ins(int ID,int nl,int nr,int L,int R,int v){ 18 if(L>R)swap(L,R); 19 if(nl>=L&&nr<=R){ 20 sum[ID]+=(nr-nl+1)*v,lazy[ID]+=v,sum[ID]%=p,lazy[ID]%=p; 21 return; 22 } 23 int m=(nl+nr)>>1; 24 if(lazy[ID])push(ID,nl,nr); 25 if(m>=L)ins(ID<<1,nl,m,L,R,v); 26 if(m<R)ins(ID<<1|1,m+1,nr,L,R,v); 27 sum[ID]=(sum[ID<<1]+sum[ID<<1|1])%p; 28 } 29 inline int query(int ID,int nl,int nr,int L,int R){ 30 if(L>R)swap(L,R); 31 if(lazy[ID])push(ID,nl,nr); 32 if(nl>=L&&nr<=R)return sum[ID]; 33 int ans=0,m=(nl+nr)>>1; 34 if(m>=L)ans+=query(ID<<1,nl,m,L,R); 35 if(m<R)ans+=query(ID<<1|1,m+1,nr,L,R); 36 sum[ID]=(sum[ID<<1]+sum[ID<<1|1])%p; 37 return ans; 38 } 39 inline void dfs1(int x){ 40 sz[x]=1; 41 for(register int i=0;i<e[x].size();i++){ 42 if(e[x][i]!=fa[x]){ 43 fa[e[x][i]]=x,dep[e[x][i]]=dep[x]+1; 44 dfs1(e[x][i]); 45 sz[x]+=sz[e[x][i]]; 46 if(sz[e[x][i]]>sz[hv[x]])hv[x]=e[x][i]; 47 } 48 } 49 } 50 inline void dfs2(int x){ 51 w[x]=++cnt; 52 ins(1,1,n,cnt,cnt,num[x]); 53 if(hv[x])top[hv[x]]=top[x],dfs2(hv[x]); 54 for(register int i=0;i<e[x].size();i++){ 55 if(e[x][i]!=fa[x]&&e[x][i]!=hv[x])top[e[x][i]]=e[x][i],dfs2(e[x][i]); 56 } 57 } 58 inline void o1(int f,int t,int v){ 59 if(dep[top[f]]<dep[top[t]])swap(f,t); 60 if(top[f]!=top[t]){ 61 ins(1,1,n,w[top[f]],w[f],v); 62 o1(fa[top[f]],t,v); 63 } 64 else ins(1,1,n,w[f],w[t],v); 65 } 66 inline void o2(int f,int t){ 67 if(dep[top[f]]<dep[top[t]])swap(f,t); 68 if(top[f]!=top[t]){ 69 an+=query(1,1,n,w[top[f]],w[f]),an%=p; 70 o2(fa[top[f]],t); 71 } 72 else { 73 an+=query(1,1,n,w[f],w[t]),an%=p; 74 printf("%d\n",an); 75 an=0; 76 } 77 } 78 inline void o3(int f,int v){ 79 ins(1,1,n,w[f],w[f]+sz[f]-1,v); 80 } 81 inline void o4(int f){ 82 printf("%d\n",query(1,1,n,w[f],w[f]+sz[f]-1)%p); 83 } 84 int main(){ 85 read(n),read(m),read(r),read(p); 86 for(register int i=1;i<=n;i++)read(num[i]); 87 register int f,t,j,x,y,z; 88 for(register int i=1;i<n;i++){ 89 read(f),read(t),e[f].push_back(t),e[t].push_back(f); 90 } 91 dep[r]=1,fa[r]=0,top[r]=r; 92 dfs1(r); dfs2(r); 93 for(register int i=1;i<=m;i++){ 94 read(j); 95 if(j==1)read(x),read(y),read(z),o1(x,y,z); 96 if(j==2)read(x),read(y),o2(x,y); 97 if(j==3)read(x),read(z),o3(x,z); 98 if(j==4)read(x),o4(x); 99 } 100 }
以上是关于树链剖分讲解&模板的主要内容,如果未能解决你的问题,请参考以下文章