树链剖分学习笔记
树链剖分学习笔记
一.前置技能:
dfs序,lca and 线段树
二.如何操作:
1.概念:(自己瞎想的)树链剖分,又叫重链剖分,是一种通过将树剖为轻重链来将树上问题转化为区间问题,解决树上两点间路径问题,以及子树问题的算法。
2.实现:
<1>.概念:
重儿子:非叶子节点儿子中,子树最大的一个。
轻儿子:非叶子节点儿子中,除重儿子以外的儿子。
而叶子节点一无所有
重链:连接非叶子节点,非叶子节点的重儿子,非叶子节点的重儿子的重儿子,非叶子节点的重儿子的重儿子的重儿子的路径……
轻边:非叶子节点与轻儿子间的连边。
<2>.性质:
重链(除了从根节点开始的)一定从一非叶子节点的轻儿子开始,到叶子节点结束。
重链之间以轻边相连。
每个点一定属于且只属于一条重链。
<3>.操作:
将一条重链看成一段区间,树上路径即可拆分为几条重链(几个区间)。
子树问题,可以利用dfs序的性质:任意一子树,可以表示为dfs序上一连续区间。
而如何解决 将一条重链看成一段区间 + dfs序 的问题?
答:有侧重的dfs序。
普通dfs序由非叶子节点x到其任意一个儿子,太浪费它的优秀性质了。
我们不妨先由x到它的重儿子,再由x到其它儿子,两个问题就都解决了。
(1).dfs1:
预处理好父亲f[x],深度d[x],子树大小siz[x],重儿子son[x]。
1 void dfs(int x,int fa) 2 { 3 f[x]=fa,d[x]=d[fa]+1,siz[x]=1; int maxa=-1,maxt=0; 4 //注意:maxt要赋值为0,因为叶子节点的son[x]为0,表示不存在重儿子,不然在dfs2会死循环 5 for(int i=head[x];i;i=e[i].nxt) 6 if(e[i].to!=fa) 7 { 8 dfs(e[i].to,x),siz[x]+=siz[e[i].to]; 9 if(maxa<siz[e[i].to]) maxt=e[i].to,maxa=siz[maxt]; 10 } 11 son[x]=maxt; 12 }
(2).dfs2:
处理出树上每个节点的dfs序num[x],用临时变量v[num[x]]储存线段树上区间[num[x],num[x]]的权值,top[x]表示x所属重链的顶端节点编号。
1 void dfs2(int x,int topx) 2 { 3 num[x]=++tot,v[tot]=w[x],top[x]=topx; 4 if(son[x]) dfs2(son[x],topx); 5 //优先搜素重儿子,若是叶子节点无重儿子则不搜,不然会死循环。 6 for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=f[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to); 7 }
(3).树剖lca:
是树上两点间修改和询问操作的基础,每次跳一条重链,在重链这段连续区间上修改或询问。
1 void LJ_update(int x,int y,int z) 2 { 3 while(top[x]!=top[y]) 4 { 5 if(d[top[x]]<d[top[y]]) swap(x,y); 6 update(1,n,num[top[x]],num[x],1,z); 7 //顶部节点top[x]的dfs序一定小于x 8 x=f[top[x]]; 9 //跳到上一条重链 10 } 11 if(d[x]>d[y]) swap(x,y); 12 update(1,n,num[x],num[y],1,z); 13 } 14 int LJ_query(int x,int y) 15 { 16 int ans=0; 17 while(top[x]!=top[y]) 18 { 19 if(d[top[x]]<d[top[y]]) swap(x,y); 20 ans=(ans+query(1,n,num[top[x]],num[x],1))%mod; 21 x=f[top[x]]; 22 } 23 if(d[x]>d[y]) swap(x,y); 24 ans=(ans+query(1,n,num[x],num[y],1))%mod; 25 return ans; 26 }
(4).子树:
非叶子节点x的子树dfs序区间为[num[x],num[x]+siz[x]-1];
1 void ZS_update(int x,int y){update(1,n,num[x],num[x]+siz[x]-1,1,y);} 2 int ZS_query(int x){return query(1,n,num[x],num[x]+siz[x]-1,1);}
总代码:
1 #include<bits/stdc++.h> 2 #define re register 3 using namespace std; 4 const int N=1e5+6; 5 int n,opt,t1,t2,t3,root,mod,m,cnt=0,tot=0; 6 int num[N],head[N],d[N],f[N],siz[N],son[N],top[N],v[N],lazy[N<<2],w[N<<2]; 7 struct edge{ 8 int nxt,to; 9 }e[N<<1]; 10 inline void add(int u,int v){e[++cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt;} 11 inline int read() 12 { 13 int T=0,F=1; char ch=getchar(); 14 while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} 15 while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); 16 return F*T; 17 } 18 void dfs(int x,int fa) 19 { 20 f[x]=fa,d[x]=d[fa]+1,siz[x]=1; int maxa=-1,maxt=0; 21 for(int i=head[x];i;i=e[i].nxt) 22 if(e[i].to!=fa) 23 { 24 dfs(e[i].to,x),siz[x]+=siz[e[i].to]; 25 if(maxa<siz[e[i].to]) maxt=e[i].to,maxa=siz[maxt]; 26 } 27 son[x]=maxt; 28 } 29 void dfs2(int x,int topx) 30 { 31 num[x]=++tot,v[tot]=w[x],top[x]=topx; 32 if(son[x]) dfs2(son[x],topx); 33 for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=f[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to); 34 } 35 void build(int l,int r,int x) 36 { 37 if(l==r){w[x]=v[l]; return;} 38 int mid=((l+r)>>1); 39 build(l,mid,x<<1),build(mid+1,r,x<<1|1); 40 w[x]=w[x<<1]+w[x<<1|1]; 41 } 42 inline void pushdown(int x,int len) 43 { 44 if(lazy[x]) 45 { 46 lazy[x<<1]=(lazy[x<<1]+lazy[x])%mod,lazy[x<<1|1]=(lazy[x<<1|1]+lazy[x])%mod; 47 w[x<<1]=(w[x<<1]+(len-(len>>1))*lazy[x])%mod,w[x<<1|1]=(w[x<<1|1]+(len>>1)*lazy[x])%mod; 48 lazy[x]=0; 49 } 50 } 51 void update(int l,int r,int p,int q,int x,int y) 52 { 53 if(p<=l&&r<=q){lazy[x]=(lazy[x]+y)%mod,w[x]=(w[x]+(r-l+1)*y)%mod; return;} 54 pushdown(x,r-l+1); 55 int mid=((l+r)>>1); 56 if(p<=mid) update(l,mid,p,q,x<<1,y); 57 if(q>mid) update(mid+1,r,p,q,x<<1|1,y); 58 w[x]=w[x<<1]+w[x<<1|1]; 59 } 60 int query(int l,int r,int p,int q,int x) 61 { 62 if(p<=l&&r<=q) return w[x]; 63 pushdown(x,r-l+1); 64 int mid=((l+r)>>1),ans=0; 65 if(p<=mid) ans=query(l,mid,p,q,x<<1); 66 if(q>mid) ans=(ans+query(mid+1,r,p,q,x<<1|1))%mod; 67 return ans; 68 } 69 void LJ_update(int x,int y,int z) 70 { 71 while(top[x]!=top[y]) 72 { 73 if(d[top[x]]<d[top[y]]) swap(x,y); 74 update(1,n,num[top[x]],num[x],1,z); 75 x=f[top[x]]; 76 } 77 if(d[x]>d[y]) swap(x,y); 78 update(1,n,num[x],num[y],1,z); 79 } 80 int LJ_query(int x,int y) 81 { 82 int ans=0; 83 while(top[x]!=top[y]) 84 { 85 if(d[top[x]]<d[top[y]]) swap(x,y); 86 ans=(ans+query(1,n,num[top[x]],num[x],1))%mod; 87 x=f[top[x]]; 88 } 89 if(d[x]>d[y]) swap(x,y); 90 ans=(ans+query(1,n,num[x],num[y],1))%mod; 91 return ans; 92 } 93 void ZS_update(int x,int y){update(1,n,num[x],num[x]+siz[x]-1,1,y);} 94 int ZS_query(int x){return query(1,n,num[x],num[x]+siz[x]-1,1);} 95 int main() 96 { 97 n=read(),m=read(),root=read(),mod=read(); 98 for(re int i=1;i<=n;++i) w[i]=read(); 99 for(re int i=1;i<n;++i) t1=read(),t2=read(),add(t1,t2),add(t2,t1); 100 //建图 101 dfs(root,0); dfs2(root,root);//预处理 102 memset(w,0,sizeof(w)); build(1,n,1);//建线段树 103 for(re int i=1;i<=m;++i) 104 { 105 opt=read(),t1=read(); 106 switch(opt) 107 { 108 case 1:t2=read(),t3=read(),LJ_update(t1,t2,t3);break; 109 case 2:t2=read(),printf("%d\n",LJ_query(t1,t2));break; 110 case 3:t2=read(),ZS_update(t1,t2);break; 111 case 4:printf("%d\n",ZS_query(t1));break; 112 } 113 } 114 return 0; 115 }