树链剖分用一句话概括就是:把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一
条链,复杂度为O(logn)
那么,树链剖分的第一步当然是对树进行轻重边的划分。
定义size(x)为以x为根的子树节点个数,令v为u的儿子中size值最大的节点,那么(u,v)就是重边,其余边为轻边。
当然,关于这个它有两个重要的性质:
(1)轻边(u,v)中,size(v)<=size(u/2)
(2)从根到某一点的路径上,不超过logn条轻边和不超过logn条重路径。
当然,剖分过程分为两次dfs,或者bfs也可以。
如果是两次dfs,那么第一次dfs就是找重边,也就是记录下所有的重边。
然后第二次dfs就是连接重边形成重链,具体过程就是:以根节点为起点,沿着重边向下拓展,拉成重链,不在当前重链上的节
点,都以该节点为起点向下重新拉一条重链。
剖分完毕后,每条重链相当于一段区间,然后用数据结构去维护,把所有重链首尾相接,放到数据结构上,然后维护整体。
在这里,当然有很多数组,现在我来分别介绍它们的作用:
siz[]数组,用来保存以x为根的子树节点个数
top[]数组,用来保存当前节点的所在链的顶端节点
son[]数组,用来保存重儿子
dep[]数组,用来保存当前节点的深度
fa[]数组,用来保存当前节点的父亲
tid[]数组,用来保存树中每个节点剖分后的新编号
rank[]数组,用来保存当前节点在线段树中的位置
那么,我们现在可以根据描述给出剖分的代码:
第一次dfs:记录所有的重边
[cpp] view plain copy print ?
- void dfs1(int u,int father,int d)
- {
- dep[u]=d;
- fa[u]=father;
- siz[u]=1;
- for(int i=head[u];~i;i=next[i])
- {
- int v=to[i];
- if(v!=father)
- {
- dfs1(v,u,d+1);
- siz[u]+=siz[v];
- if(son[u]==-1||siz[v]>siz[son[u]])
- son[u]=v;
- }
- }
- }
void dfs1(int u,int father,int d)
{
dep[u]=d;
fa[u]=father;
siz[u]=1;
for(int i=head[u];~i;i=next[i])
{
int v=to[i];
if(v!=father)
{
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
son[u]=v;
}
}
}
第二次dfs:连重边成重链
[cpp] view plain copy print ?
- void dfs2(int u,int tp)
- {
- top[u]=tp;
- tid[u]=++tim;
- rank[tid[u]]=u;
- if(son[u]==-1) return;
- dfs2(son[u],tp);
- for(int i=head[u];~i;i=next[i])
- {
- int v=to[i];
- if(v!=son[u]&&v!=fa[u])
- dfs2(v,v);
- }
- }
void dfs2(int u,int tp)
{
top[u]=tp;
tid[u]=++tim;
rank[tid[u]]=u;
if(son[u]==-1) return;
dfs2(son[u],tp);
for(int i=head[u];~i;i=next[i])
{
int v=to[i];
if(v!=son[u]&&v!=fa[u])
dfs2(v,v);
}
}
当然,由于题目有时候要求边很多,所以最好不要用二维数组表示边,应用邻接表或者链式前向星。
当然,这里面有一个重要的操作,那就是修改树中边权的值。
如何修改u到v的边权的值呢?这里有两种情况:
(1)如果u与v在同一条重链上,那么就直接修改了
(2)如果u与v不在同一条重链上,那么就一边进行修改,一边将u与v往同一条重链上靠,这样就变成了第一种情况了
那么现在的关键问题就是如何将u和v往同一条重链上靠?这个问题此处我就省略了。
至此,树链剖分原理基本分析完毕!
HDU3966(树链剖分)
分类: 数据结构
2013-08-30 14:15
382人阅读
收藏
举报
题目:Aragorn's Story
题意:给一棵树,并给定各个点权的值,然后有3种操作:
I C1 C2 K: 把C1与C2的路径上的所有点权值加上K
D C1 C2 K:把C1与C2的路径上的所有点权值减去K
Q C:查询节点编号为C的权值
分析:典型的树链剖分题目,先进行剖分,然后用线段树去维护即可,注意HDU的OJ采用Windows系统,容易爆栈,所以在代码
前面加上:#pragma comment(linker, "/STACK:1024000000,1024000000")进行手动扩栈。
[cpp] view plain copy print ?
- #pragma comment(linker, "/STACK:1024000000,1024000000")
- #include <iostream>
- #include <string.h>
- #include <algorithm>
- #include <stdio.h>
- #include <vector>
- using namespace std;
- const int N=50010;
- int n,m,Q;
- int tim;
- int num[N],siz[N],top[N],son[N];
- int dep[N],tid[N],rank[N],fa[N];
- int head[N],to[2*N],next[2*N],edge;
- void Init()
- {
- memset(head,-1,sizeof(head));
- memset(son,-1,sizeof(son));
- tim=0;
- edge=0;
- }
- void addedge(int u,int v)
- {
- to[edge]=v,next[edge]=head[u],head[u]=edge++;
- to[edge]=u,next[edge]=head[v],head[v]=edge++;
- }
-
- void dfs1(int u,int father,int d)
- {
- dep[u]=d;
- fa[u]=father;
- siz[u]=1;
- for(int i=head[u];~i;i=next[i])
- {
- int v=to[i];
- if(v!=father)
- {
- dfs1(v,u,d+1);
- siz[u]+=siz[v];
- if(son[u]==-1||siz[v]>siz[son[u]])
- son[u]=v;
- }
- }
- }
- void dfs2(int u,int tp)
- {
- top[u]=tp;
- tid[u]=++tim;
- rank[tid[u]]=u;
- if(son[u]==-1) return;
- dfs2(son[u],tp);
- for(int i=head[u];~i;i=next[i])
- {
- int v=to[i];
- if(v!=son[u]&&v!=fa[u])
- dfs2(v,v);
- }
- }
-
- #define lson l,mid,rt<<1
- #define rson mid+1,r,rt<<1|1
- int sum[4*N],col[4*N];
- void PushUP(int rt)
- {
- sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
- }
- void PushDown(int rt,int m)
- {
- if(col[rt])
- {
- col[rt<<1]+=col[rt];
- col[rt<<1|1]+=col[rt];
- sum[rt<<1]+=(m-(m>>1))*col[rt];
- sum[rt<<1|1]+=(m>>1)*col[rt];
- col[rt]=0;
- }
- }
- void Build(int l,int r,int rt)
- {
- col[rt]=0;
- if(l==r)
- {
- sum[rt]=num[rank[l]];
- return;
- }
- int mid=(l+r)>>1;
- Build(lson);
- Build(rson);
- PushUP(rt);
- }
- void Update(int L,int R,int v,int l,int r,int rt)
- {
- if(L<=l&&R>=r)
- {
- col[rt]+=v;
- sum[rt]+=v*(r-l+1);
- return;
- }
- PushDown(rt,r-l+1);
- int mid=(l+r)>>1;
- if(L<=mid)
- Update(L,R,v,lson);
- if(R>mid)
- Update(L,R,v,rson);
- PushUP(rt);
- }
- int Query(int l,int r,int rt,int val)
- {
- if(l==r)
- return sum[rt];
- PushDown(rt,r-l+1);
- int mid=(l+r)>>1;
- int ret=0;
- if(val<=mid) ret=Query(lson,val);
- else ret=Query(rson,val);
- PushUP(rt);
- return ret;
- }
- void Change(int x,int y,int val)
- {
- while(top[x]!=top[y])
- {
- if(dep[top[x]]<dep[top[y]]) swap(x,y);
- Update(tid[top[x]],tid[x],val,1,n,1);
- x=fa[top[x]];
- }
- if(dep[x]>dep[y]) swap(x,y);
- Update(tid[x],tid[y],val,1,n,1);
- }
- int main()
- {
- char oper[5];
- int a,b,c;
- while(~scanf("%d%d%d",&n,&m,&Q))
- {
- Init();
- for(int i=1;i<=n;i++)
- scanf("%d",&num[i]);
- for(int i=1;i<=m;i++)
- {
- scanf("%d%d",&a,&b);
- addedge(a,b);
- }
- dfs1(1,0,0);
- dfs2(1,1);
- Build(1,n,1);
- while(Q--)
- {
- scanf("%s",oper);
- if(oper[0]=='Q')
- {
- scanf("%d",&a);
- printf("%d\n",Query(1,n,1,tid[a]));
- }
- else
- {
- scanf("%d%d%d",&a,&b,&c);
- if(oper[0]=='D') c=-c;
- Change(a,b,c);
- }
- }
- }
- return 0;
- }