现在的位置: 首页 > 综合 > 正文

树链剖分原理 树链剖分原理         HDU3966(树链剖分)

2017年09月28日 ⁄ 综合 ⁄ 共 4232字 ⁄ 字号 评论关闭

树链剖分原理

分类:
数据结构
232人阅读评论(0)收藏举报

树链剖分用一句话概括就是:把一棵树剖分为若干条链,然后利用数据结构(树状数组,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:记录所有的重边

  1. void dfs1(int u,int father,int d)
  2. {
  3. dep[u]=d;
  4. fa[u]=father;
  5. siz[u]=1;
  6. for(int i=head[u];~i;i=next[i])
  7. {
  8. int v=to[i];
  9. if(v!=father)
  10. {
  11. dfs1(v,u,d+1);
  12. siz[u]+=siz[v];
  13. if(son[u]==-1||siz[v]>siz[son[u]])
  14. son[u]=v;
  15. }
  16. }
  17. }

第二次dfs:连重边成重链

  1. void dfs2(int u,int tp)
  2. {
  3. top[u]=tp;
  4. tid[u]=++tim;
  5. rank[tid[u]]=u;
  6. if(son[u]==-1) return;
  7. dfs2(son[u],tp);
  8. for(int i=head[u];~i;i=next[i])
  9. {
  10. int v=to[i];
  11. if(v!=son[u]&&v!=fa[u])
  12. dfs2(v,v);
  13. }
  14. }

当然,由于题目有时候要求边很多,所以最好不要用二维数组表示边,应用邻接表或者链式前向星。

当然,这里面有一个重要的操作,那就是修改树中边权的值。

如何修改u到v的边权的值呢?这里有两种情况:

(1)如果u与v在同一条重链上,那么就直接修改了

(2)如果u与v不在同一条重链上,那么就一边进行修改,一边将u与v往同一条重链上靠,这样就变成了第一种情况了

那么现在的关键问题就是如何将u和v往同一条重链上靠?这个问题此处我就省略了。

至此,树链剖分原理基本分析完毕!

        HDU3966(树链剖分)

分类:
数据结构
382人阅读评论(0)收藏举报

题目: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")进行手动扩栈。

  1. #pragma comment(linker, "/STACK:1024000000,1024000000")
  2. #include <iostream>
  3. #include <string.h>
  4. #include <algorithm>
  5. #include <stdio.h>
  6. #include <vector>
  7. using namespace std;
  8. const int N=50010;
  9. int n,m,Q;
  10. int tim;
  11. int num[N],siz[N],top[N],son[N];
  12. int dep[N],tid[N],rank[N],fa[N];
  13. int head[N],to[2*N],next[2*N],edge;
  14. void Init()
  15. {
  16. memset(head,-1,sizeof(head));
  17. memset(son,-1,sizeof(son));
  18. tim=0;
  19. edge=0;
  20. }
  21. void addedge(int u,int v)
  22. {
  23. to[edge]=v,next[edge]=head[u],head[u]=edge++;
  24. to[edge]=u,next[edge]=head[v],head[v]=edge++;
  25. }
  26. //树链剖分部分
  27. void dfs1(int u,int father,int d)
  28. {
  29. dep[u]=d;
  30. fa[u]=father;
  31. siz[u]=1;
  32. for(int i=head[u];~i;i=next[i])
  33. {
  34. int v=to[i];
  35. if(v!=father)
  36. {
  37. dfs1(v,u,d+1);
  38. siz[u]+=siz[v];
  39. if(son[u]==-1||siz[v]>siz[son[u]])
  40. son[u]=v;
  41. }
  42. }
  43. }
  44. void dfs2(int u,int tp)
  45. {
  46. top[u]=tp;
  47. tid[u]=++tim;
  48. rank[tid[u]]=u;
  49. if(son[u]==-1) return;
  50. dfs2(son[u],tp);
  51. for(int i=head[u];~i;i=next[i])
  52. {
  53. int v=to[i];
  54. if(v!=son[u]&&v!=fa[u])
  55. dfs2(v,v);
  56. }
  57. }
  58. //线段树部分
  59. #define lson l,mid,rt<<1
  60. #define rson mid+1,r,rt<<1|1
  61. int sum[4*N],col[4*N];
  62. void PushUP(int rt)
  63. {
  64. sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
  65. }
  66. void PushDown(int rt,int m)
  67. {
  68. if(col[rt])
  69. {
  70. col[rt<<1]+=col[rt];
  71. col[rt<<1|1]+=col[rt];
  72. sum[rt<<1]+=(m-(m>>1))*col[rt];
  73. sum[rt<<1|1]+=(m>>1)*col[rt];
  74. col[rt]=0;
  75. }
  76. }
  77. void Build(int l,int r,int rt)
  78. {
  79. col[rt]=0;
  80. if(l==r)
  81. {
  82. sum[rt]=num[rank[l]];
  83. return;
  84. }
  85. int mid=(l+r)>>1;
  86. Build(lson);
  87. Build(rson);
  88. PushUP(rt);
  89. }
  90. void Update(int L,int R,int v,int l,int r,int
    rt)
  91. {
  92. if(L<=l&&R>=r)
  93. {
  94. col[rt]+=v;
  95. sum[rt]+=v*(r-l+1);
  96. return;
  97. }
  98. PushDown(rt,r-l+1);
  99. int mid=(l+r)>>1;
  100. if(L<=mid)
  101. Update(L,R,v,lson);
  102. if(R>mid)
  103. Update(L,R,v,rson);
  104. PushUP(rt);
  105. }
  106. int Query(int l,int r,int rt,int val)
  107. {
  108. if(l==r)
  109. return sum[rt];
  110. PushDown(rt,r-l+1);
  111. int mid=(l+r)>>1;
  112. int ret=0;
  113. if(val<=mid) ret=Query(lson,val);
  114. else ret=Query(rson,val);
  115. PushUP(rt);
  116. return ret;
  117. }
  118. void Change(int x,int y,int val)
  119. {
  120. while(top[x]!=top[y])
  121. {
  122. if(dep[top[x]]<dep[top[y]]) swap(x,y);
  123. Update(tid[top[x]],tid[x],val,1,n,1);
  124. x=fa[top[x]];
  125. }
  126. if(dep[x]>dep[y]) swap(x,y);
  127. Update(tid[x],tid[y],val,1,n,1);
  128. }
  129. int main()
  130. {
  131. char oper[5];
  132. int a,b,c;
  133. while(~scanf("%d%d%d",&n,&m,&Q))
  134. {
  135. Init();
  136. for(int i=1;i<=n;i++)
  137. scanf("%d",&num[i]);
  138. for(int i=1;i<=m;i++)
  139. {
  140. scanf("%d%d",&a,&b);
  141. addedge(a,b);
  142. }
  143. dfs1(1,0,0);
  144. dfs2(1,1);
  145. Build(1,n,1);
  146. while(Q--)
  147. {
  148. scanf("%s",oper);
  149. if(oper[0]=='Q')
  150. {
  151. scanf("%d",&a);
  152. printf("%d\n",Query(1,n,1,tid[a]));
  153. }
  154. else
  155. {
  156. scanf("%d%d%d",&a,&b,&c);
  157. if(oper[0]=='D') c=-c;
  158. Change(a,b,c);
  159. }
  160. }
  161. }
  162. return 0;
  163. }  

抱歉!评论已关闭.