树链剖分
树链剖分的实质:将一棵树划分成若干条链,用数据结构去维护每条链。
如:把边或点哈希到线段树(或其他数据结构)上进行维护和查询。
这样做的理由:从根到某一点的路径上,不超过logn条轻边和不超过logn条重路径。
下面开始介绍树链剖分的做法:
size(u)为以u为根的子树节点个数(包括u),令v为u的儿子中size值最大的节点,那么(u,v)就是重边,其余边为轻边。
Ps:从u开始走向儿子的重边只有一条!所以有多个size值最大儿子时,选其中一个即可。
所以有下性质:
1.轻边(U,V),size(V)<=size(U)/2。
2.从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。
这个两个性质是树链剖分树链操作的时间复杂度的保证。
树链剖分所需要维护的信息:
sz[x]:以x为根节点的节点数(包括x)
fa[x]:x的父亲节点
deep[x]:x的深度
son[x]:x的重儿子
top[x]:x所在链的顶端节点
id[x]:点x树链剖分后在线段树上的位置
hid[x]:线段树上位置为x的点在树上的位置(与id[x]为逆运算)
树链剖分具体过程:
第一遍dfs:求出sz,fa,deep,son数组。
第二遍dfs:求出top,id,hid数组。
然后是边权或点权信息的维护:
修改x到y的路径上的信息:
(1)如果u与v在同一条重链上,直接修改即可
(2)如果u与v不在同一条重链上,那么就一边进行修改,一边将u与v往同一条重链上靠,之后转化成第一种情况。
查询x到y路径上的信息同理。
Ps: 当处理边权时,每条边在线段树上的位置,由深度大的节点的id值表示。
当处理点权时,每个点在线段树上的位置,由该点的id值表示。
然后。。。。没有然后了。。
模板:
1 vector<int>mp[K]; 2 int top[K],sz[K],fa[K],son[K],id[K],hid[K],deep[K]; 3 int cnt,add[4*K]; 4 5 int query(int o,int l,int r,int x) 6 { 7 if(l==r) return add[o]; 8 int mid=l+r>>1; 9 if(x<=mid) return query(o<<1,l,mid,x)+add[o]; 10 return query(o<<1|1,mid+1,r,x)+add[o]; 11 } 12 int update(int o,int l,int r,int nl,int nr,int v) 13 { 14 if(l==nl&&r==nr) 15 return add[o]+=v; 16 int mid=l+r>>1; 17 if(nr<=mid) update(o<<1,l,mid,nl,nr,v); 18 else if(nl>mid) update(o<<1|1,mid+1,r,nl,nr,v); 19 else update(o<<1,l,mid,nl,mid,v),update(o<<1|1,mid+1,r,mid+1,nr,v); 20 } 21 void dfs1(int x,int f) 22 { 23 sz[x]=1,fa[x]=f,son[x]=-1,deep[x]=deep[f]+1; 24 for(int i=0;i<mp[x].size();i++) 25 if(mp[x][i]!=f) 26 { 27 dfs1(mp[x][i],x); 28 sz[x]+=sz[mp[x][i]]; 29 if(son[x]==-1||sz[son[x]]<sz[mp[x][i]]) 30 son[x]=mp[x][i]; 31 } 32 } 33 void dfs2(int x,int f) ///每条边用深度大的节点的序号表示 34 { 35 top[x]=f,id[x]=++cnt,hid[id[x]]=x; 36 if(son[x]!=-1) dfs2(son[x],f); 37 for(int i=0;i<mp[x].size();i++) 38 if(mp[x][i]!=fa[x]&&mp[x][i]!=son[x]) 39 dfs2(mp[x][i],mp[x][i]); 40 } 41 void tree_update(int x,int y,int v) 42 { 43 while(top[x]!=top[y]) 44 { 45 if(deep[top[x]]<deep[top[y]]) swap(x,y); 46 update(1,1,cnt,id[top[x]],id[x],v);//更新x-top[x]-fa[top[x]] 47 x=fa[top[x]];//所以直接fa[top[x]] 48 } 49 if(x==y) return;//点修改请注释这句 50 if(deep[x]>deep[y]) swap(x,y); 51 update(1,1,cnt,id[son[x]],id[y],v);//点修改请改为id[x] 52 }
作者:weeping
出处:www.cnblogs.com/weeping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。