树链剖分
树链剖分有什么用
我们经常会写到这样的毒瘤题目:给你一棵树,每次对树上的一条链进行操作。
例如洛咕:P3384 【模板】树链剖分。
实现方法
那么我们怎么很快的处理这样的问题呢?
学过倍增LCA的同学应该清楚,就算是倍增,也只能找出它的几代父亲,而不能对路径上的每一个点进行修改。
我们发现对于树上链的操作是很复杂的,因为树上的节点储存是不连续的。
然后你就能拍脑袋了。
既然不连续的区间修改起来很麻烦,那么有没有方法把它变成连续的呢?
我们发现对于一棵树,我们对节点的储存顺序是没有要求的。
对于一个父节点,我们只需要知道他儿子们的储存位置;对于一个子节点,我们只要知道他父亲的储存位置,我们就可以存储这棵树了。
那么我们可以通过给这棵树进行重标号,把树上的链化为连续的区间,我们就能很轻松地处理一段链的修改,查询操作了。
下面有几个定义:
1.重儿子:一个节点的儿子节点里面,以重儿子为根的子树大小最大。
2.轻儿子:一个节点的儿子节点,除了唯一一个重儿子,其余儿子为轻儿子。
3.重边:连接父亲和重儿子的边称为重边。
4.轻边:连接父亲和轻儿子的边称为轻边。
5.重链:只由重边构成的链称为重链。
我们只要把一棵树上的重链全部按顺序储存,就能把树上的问题转化为区间上的问题。
那么接下来用线段树就能轻松维护这个区间问题了。
时间复杂度证明
我们已经学会给树解剖了(雾)
那么我们怎么知道按照重链解剖,时间复杂度会变得更优呢?
1.每走过一个轻边,子树的节点数至少减少一半
我们假设树上一个节点有$n$个儿子,那么由于重儿子子树大小是最大的,当轻儿子的子树大小最大的时候,轻儿子的子树大小应该为\frac{1}{n}$。
我们可以用抽屉原理证明。
那么轻儿子子树大小之多为父亲的$\frac{1}{2}$
2.一条链上最多有$2logn$条重链
这个也是很好证明的。(虽然是伪证)
我们考虑什么时候链最多。
我们先考虑半链。
发现重链的数量只和轻边有关。
因为重链的两端所连的节点不可能再连接其他的重边,不然长度就会增加。
那么一段链上的重链数就等于轻边数$+1$。
根据结论$1$,一个节点数为$n$的树,最多走$logn$次轻边,节点数就会变成$1$。
换句话说,一条半链上最多有$logn$条轻边。
那么一条全链上最多有$2logn$条重链。
3.单次修改,查询的操作为$log^2n$
我们每次查询可以修改一条链从链底到链顶的一段区间。
根据结论$2$我们知道,单次修改的线段树操作次数为$logn$次,而线段树的时间复杂度为$logn$。
总的时间复杂度就是$log^2n$
具体的实现细节
我们先对树进行预处理,把树重标号以及剖分出重链。
这个预处理由两个dfs构成。
第一个dfs,我们可以计算出以下几个值。
1.每个节点的子树大小$siz[x]$
2.每个节点的深度$dep[x]$
3.每个节点的父亲$fa[x]$
4.每个节点的重儿子$son[x]$
1 void dfs1(int x,int pre,int deep){ 2 dep[x]=deep;siz[x]=1; 3 for(int i=head[x];i;i=nxt[i]){ 4 if(ver[i]==pre)continue; 5 fa[ver[i]]=x; 6 dfs1(ver[i],x,deep+1); 7 siz[x]+=siz[ver[i]]; 8 if(siz[son[x]]<siz[ver[i]])son[x]=ver[i]; 9 } 10 }
然后第二个dfs,我们给每个节点重新标号。
统计出以下数组。
1.每个节点在线段树上的位置$id[x]$
2.线段树某个位置上的权值$rw[x]$
3.每个节点所在链的链顶节点$top[x]$
为了保证重链上的点储存位置连续,我们要先对重链进行标号。
1 void dfs2(int x,int ltp){ 2 id[x]=++cnt; 3 a[cnt]=w[x]; 4 top[x]=ltp; 5 if(son[x])dfs2(son[x],ltp); 6 for(int i=head[x];i;i=nxt[i]){ 7 if(ver[i]==son[x]||ver[i]==fa[x])continue; 8 dfs2(ver[i],ver[i]); 9 } 10 }
可能比较抽象,我们可以通过一张图来看。
红色框住的为重链,黑色标号为原来的编号,棕色标号为线段树位置。
我们很容易发现重链上的节点是连续的,子树内的节点是连续的。
那么我们就很容易进行维护了。
至于线段树部分,就不详细解释了。直接看代码就好了
代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=1e5+100; 4 int read(){ 5 char c;int num,f=1; 6 while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0'; 7 while(c=getchar(), isdigit(c))num=num*10+c-'0'; 8 return f*num; 9 } 10 int mod=1e9+7; 11 int n,m,r,p; 12 int w[N],dep[N],fa[N],son[N],id[N],top[N]; 13 int head[N],nxt[N*2],ver[N*2],tot=1,siz[N],cnt=0; 14 void up(int &x,int y){ 15 x+=y; 16 if(x>=mod)x-=mod; 17 } 18 //线段树 19 int laz[N*4],tree[N*4],a[N]; 20 /*void build(int l,int r,int rt){ 21 for(int i=1;i<=n;i++)tree[i]=a[i]; 22 } 23 void modify(int l,int r,int L,int R,int rt,int x){ 24 for(int i=L;i<=R;i++)up(tree[i],x); 25 } 26 int query(int l,int r,int L,int R,int rt){ 27 int ans=0; 28 for(int i=L;i<=R;i++)up(ans,tree[i]); 29 return ans; 30 }*/ 31 32 void update(int rt){ 33 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 34 if(tree[rt]>=mod)tree[rt]-=mod; 35 } 36 void build(int l,int r,int rt){ 37 if(l==r){tree[rt]=a[l]%mod;return ;} 38 int mid=(l+r)>>1; 39 build(l,mid,rt<<1); 40 build(mid+1,r,rt<<1|1); 41 update(rt); 42 } 43 void pushdown(int rt,int len){ 44 up(laz[rt<<1],laz[rt]); 45 up(tree[rt<<1],laz[rt]*(len-(len>>1))%mod); 46 up(laz[rt<<1|1],laz[rt]); 47 up(tree[rt<<1|1],laz[rt]*(len>>1)%mod); 48 laz[rt]=0; 49 } 50 int query(int l,int r,int L,int R,int rt){ 51 if(L<=l&&r<=R)return tree[rt]; 52 int mid=(l+r)>>1,ans=0; 53 pushdown(rt,r-l+1); 54 if(L<=mid)up(ans,query(l,mid,L,R,rt<<1)); 55 if(mid+1<=R)up(ans,query(mid+1,r,L,R,rt<<1|1)); 56 return ans; 57 } 58 void modify(int l,int r,int L,int R,int rt,int x){ 59 if(L<=l&&r<=R){ 60 up(tree[rt],x*(r-l+1)%mod); 61 up(laz[rt],x); 62 return ; 63 } 64 int mid=(l+r)>>1; 65 pushdown(rt,r-l+1); 66 if(L<=mid)modify(l,mid,L,R,rt<<1,x); 67 if(mid+1<=R)modify(mid+1,r,L,R,rt<<1|1,x); 68 update(rt); 69 } 70 71 72 void add_edge(int u,int v){ 73 ver[++tot]=v;nxt[tot]=head[u];head[u]=tot; 74 ver[++tot]=u;nxt[tot]=head[v];head[v]=tot; 75 } 76 void dfs1(int x,int pre,int deep){ 77 dep[x]=deep;siz[x]=1; 78 for(int i=head[x];i;i=nxt[i]){ 79 if(ver[i]==pre)continue; 80 fa[ver[i]]=x; 81 dfs1(ver[i],x,deep+1); 82 siz[x]+=siz[ver[i]]; 83 if(siz[son[x]]<siz[ver[i]])son[x]=ver[i]; 84 } 85 } 86 void dfs2(int x,int ltp){ 87 id[x]=++cnt; 88 a[cnt]=w[x]; 89 top[x]=ltp; 90 if(son[x])dfs2(son[x],ltp); 91 for(int i=head[x];i;i=nxt[i]){ 92 if(ver[i]==son[x]||ver[i]==fa[x])continue; 93 dfs2(ver[i],ver[i]); 94 } 95 } 96 void link_modify(int x,int y,int val){ 97 while(top[x]!=top[y]){ 98 if(dep[top[x]]<dep[top[y]])swap(x,y); 99 modify(1,n,id[top[x]],id[x],1,val); 100 x=fa[top[x]]; 101 } 102 if(dep[x]<dep[y])swap(x,y); 103 modify(1,n,id[y],id[x],1,val); 104 } 105 int link_query(int x,int y){ 106 int ans=0; 107 while(top[x]!=top[y]){ 108 if(dep[top[x]]<dep[top[y]])swap(x,y); 109 up(ans,query(1,n,id[top[x]],id[x],1)); 110 x=fa[top[x]]; 111 } 112 if(dep[x]<dep[y])swap(x,y); 113 up(ans,query(1,n,id[y],id[x],1)); 114 return ans; 115 } 116 void tree_modify(int x,int val){ 117 modify(1,n,id[x],id[x]+siz[x]-1,1,val%mod); 118 } 119 int tree_query(int x){ 120 return query(1,n,id[x],id[x]+siz[x]-1,1); 121 } 122 int main() 123 { 124 //freopen("data.in","r",stdin); 125 n=read();m=read(); 126 r=read();mod=read(); 127 for(int i=1;i<=n;i++)w[i]=read(); 128 for(int i=1;i< n;i++)add_edge(read(),read()); 129 dfs1(r,r,1);dfs2(r,r);build(1,n,1); 130 for(int i=1;i<=m;i++){ 131 int opt=read(),x,y,z; 132 if(opt==1){ 133 x=read();y=read();z=read(); 134 link_modify(x,y,z); 135 }else if(opt==2){ 136 x=read();y=read(); 137 printf("%d\n",link_query(x,y)); 138 }else if(opt==3){ 139 x=read();y=read(); 140 tree_modify(x,y); 141 }else if(opt==4){ 142 x=read(); 143 printf("%d\n",tree_query(x)); 144 } 145 } 146 //cout<<siz[1]<<endl; 147 /*for(int i=1;i<=10;i++)a[i]=i; 148 build(1,10,1); 149 modify(1,10,1,10,1,10); 150 cout<<query(1,10,1,3,1)<<endl; 151 modify(1,10,2,9,1,10); 152 cout<<query(1,10,1,3,1)<<endl; 153 modify(1,10,1,2,1,10); 154 cout<<query(1,10,1,3,1)<<endl;*/ 155 return 0; 156 }