树链剖分讲解&模板

原理&实现

 线段树是一种可以快速进行区间修改和查询的数据结构,并且我们已经知道可以通过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)

Luogu P3384

  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 }
不要点人家嘛つ﹏⊂

猜你喜欢

转载自www.cnblogs.com/Fang-Hao/p/9026978.html