树链剖分

树链剖分的实质:将一棵树划分成若干条链,用数据结构去维护每条链。

  如:把边或点哈希到线段树(或其他数据结构)上进行维护和查询。

这样做的理由:从根到某一点的路径上,不超过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 }

 

posted @ 2017-05-17 20:38  weeping  阅读(259)  评论(0编辑  收藏  举报