初学树剖的一些理解

来源:互联网 发布:世纪佳缘 知乎 编辑:程序博客网 时间:2024/06/03 03:27

省选前刚学了树剖的模板,感觉刚学时看网上的讲解觉得十分头晕,似乎又有很多的乱七八糟的数组,代码又很长,省选真的考到树剖也没能敢打。现在把对树剖的一些理解写下来,毕竟刚学,理解不一定准确,也可能会有一些错误,欢迎各位大佬评论自己的见解或指出我的错误。

 

树链剖分,就是把树放进线段树里。既然是放在线段树里了,那么求最大值求和还有修改等等都能实现。

字面上来说,树链,是树上的路径;剖分,就是把路径分类为重链和轻链。

我们设一个节点u的儿子为x1,x2,x3...,以这些儿子为根的子树中节点个数(设为size[])最多的是size[xi],则xiu的重儿子,u的其他儿子为轻儿子。

重边:点u与其重儿子的连边。
轻边:点u与其轻儿子的连边。
重链:由重边连成的路径。
轻链:由轻边连成的路径。

 

重链轻链的基本性质:

(1)轻边(u,v)中,size(v)<=size(u)/2

(2)从根到某一点的路径上,轻链、重链的个数都不大于logn。

 

假如设一个点u所在重链的最顶端的点为top[u],另一个点v所在重链的最顶端的点为top[v],当top[u]==top[v]时,一定有u为v的祖先或v为u的祖先。那么如果在预处理的时候把top和dep都求出来,那么就可以用重链u和v求LCA——先判断top[u]、top[v]是否相等,若不相等就判断dep[top[u]]、dep[top[v]]大小关系,假若dep[top[u]]>=dep[top[v]],那么u=fa[top[u]](dep[top[v]]>=dep[top[u]]时同理),然后继续判断top[u]、top[v]是否相等,直到相等后就可以直接比较dep求得LCA(感觉并不能比一般的倍增快,但是树剖要用此种方式)

 

下图中加粗的就是重链,加红点的就是每条重链的top(注意5、12、8、10)


要按照什么顺序把树的边或点放进线段树呢?又和重链轻链有什么关系呢?

设每个点放在线段树里的位置为id,首先,我们肯定尽量把父亲和儿子的id排在相邻的位置上,若父亲的id永远小于其儿子和后代,但是每个u肯定只能和它一个儿子的id相邻,那么这个儿子必须很特殊,那么不如用重儿子做这个特殊的儿子。那么其他儿子怎么办,如果按次排在重儿子之后的话,那么树上的链的id很难连续,自然不利于修改查询,为了满足需要,我们可以先偏心地满足重儿子的需求,把重儿子的子树排完后,再排其他不受重视的轻儿子。以此类推,id相当于是这么排的:我们每排到一个点u时,我们会先排u的重儿子和重儿子的子树中的点,然后再排其他儿子(这些轻儿子的顺序无所谓)。

上图中排的顺序就是:

Id:       1   2   3   4   5   6   7   8   9   10   11   12   13   14

点的编号: 1   4   9   13  14  8   10  3   7   2    6    11   12   6

这样的好处是:每颗子树的id排在一起,每条重链的id排在一起。放进线段树里的时候,也可以和上面所说的求LCA的方法相配合,查询修改两点间路径的信息,那么就可以在上面求LCA的过程中逐步更新答案(毕竟在线段树里一条重链的id是连续的)

 

其实树剖不一定把重儿子做特殊的儿子,根据不同的题目,可以是不同的。

代码如下:题目链接(洛谷)

//Serene#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>using namespace std;const int maxn=100000+10;long long n,m,root,mod,ans,val[maxn];long long size[maxn],son[maxn],dep[maxn],fa[maxn];//son重儿子 long long aa;char cc;long long read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa;}int to[2*maxn],fir[maxn],nxt[2*maxn],e=0;void lianjie(int x,int y) {to[++e]=y;nxt[e]=fir[x];fir[x]=e;to[++e]=x;nxt[e]=fir[y];fir[y]=e;}//求fa、size、son void dfs1(int pos,int f,int d) {fa[pos]=f; size[pos]=1; dep[pos]=d;if(!fir[pos]) return ;for(int y=fir[pos];y;y=nxt[y]) {if(to[y]==f) continue;dfs1(to[y],pos,d+1);size[pos]+=size[to[y]];if(size[to[y]]>size[son[pos]]) son[pos]=to[y];}}//求id、top、end。end[i]表示以i为根的子树的最后一个id long long id[maxn],top[maxn],v[maxn],end[maxn],num=0;void dfs2(int pos,int t) {id[pos]=++num;top[pos]=t;if(son[pos]) dfs2(son[pos],t),end[son[pos]]=num;for(int y=fir[pos];y;y=nxt[y]) {if(to[y]==fa[pos]||to[y]==son[pos]) continue;dfs2(to[y],to[y]);end[to[y]]=num;}}struct Tree{int l,r;long long num,laz;}tree[4*maxn];void bld(int pos,int l,int r) {tree[pos].l=l;tree[pos].r=r;if(l==r) {tree[pos].num=v[l];return;}int mid=(l+r)>>1;bld(pos<<1,l,mid);bld(pos<<1|1,mid+1,r);tree[pos].num=(tree[pos<<1].num+tree[pos<<1|1].num)%mod;}long long query(int pos,int l,int r) {if(tree[pos].laz) {if(tree[pos].l!=tree[pos].r) {tree[pos<<1].laz=(tree[pos<<1].laz+tree[pos].laz)%mod;tree[pos<<1|1].laz=(tree[pos<<1|1].laz+tree[pos].laz)%mod;}tree[pos].num=(tree[pos].num+tree[pos].laz*(tree[pos].r-tree[pos].l+1))%mod;tree[pos].laz=0;}if(tree[pos].l==l&&tree[pos].r==r) return tree[pos].num;int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid) return query(pos<<1,l,r);if(l>mid) return query(pos<<1|1,l,r);return (query(pos<<1,l,mid)+query(pos<<1|1,mid+1,r))%mod;}void chge(int pos,int l,int r,int x) {tree[pos].num=(tree[pos].num+(r-l+1)*x)%mod;if(tree[pos].l==l&&tree[pos].r==r) {if(l!=r) {tree[pos<<1].laz=(tree[pos<<1].laz+x)%mod;tree[pos<<1|1].laz=(tree[pos<<1|1].laz+x)%mod;}return;}int mid=(tree[pos].l+tree[pos].r)>>1;if(r<=mid) chge(pos<<1,l,r,x);else if(l>mid) chge(pos<<1|1,l,r,x);else chge(pos<<1,l,mid,x),chge(pos<<1|1,mid+1,r,x);}//在求LCA的过程中更新答案 void Yth(int x,int y,int z) {int tp1=top[x],tp2=top[y];while(tp1!=tp2) {if(dep[tp1]<dep[tp2]) swap(tp1,tp2),swap(x,y);if(!z) ans=(ans+query(1,id[tp1],id[x]))%mod;else chge(1,id[tp1],id[x],z);x=fa[tp1];tp1=top[x];}if(x==y) {if(!z) ans=(ans+query(1,id[x],id[x]))%mod;else chge(1,id[x],id[x],z);return;}if(dep[x]<dep[y]) swap(x,y);if(!z) ans=(ans+query(1,id[y],id[x]))%mod;else chge(1,id[y],id[x],z);}int main() {n=read();m=read();root=read();mod=read();for(int i=1;i<=n;++i) val[i]=read()%mod;int k,x,y,z;for(int i=1;i<n;++i) {x=read();y=read();lianjie(x,y);}dfs1(root,0,1); dfs2(root,root); end[root]=num;//printf("id: ");for(int i=1;i<=n;++i) printf("%lld ",id[i]);printf("\n");for(int i=1;i<=n;++i) v[id[i]]=val[i];//printf("V: ");for(int i=1;i<=n;++i) printf("%lld ",v[i]);printf("\n");bld(1,1,n);//printf("end: ");for(int i=1;i<=n;++i) printf("%lld ",end[i]);printf("\n");for(int i=1;i<=m;++i) {k=read();if(k==1) {x=read();y=read();z=read();Yth(x,y,z);}else if(k==2) {x=read();y=read();ans=0; Yth(x,y,0);printf("%lld\n",ans%mod);}else if(k==3) {x=read();z=read();chge(1,id[x],end[x],z);}else {x=read();printf("%lld\n",query(1,id[x],end[x]));}//printf("NOW: ");//for(int i=1;i<=n;++i) //  printf("%lld ",query(1,id[i],id[i]));printf("\n");}return 0;}/*5 5 2 247 3 7 8 0 1 21 53 14 13 4 23 2 24 51 5 1 32 1 3*/


0 0
原创粉丝点击