[0.x]前言

树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。
具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。
──OI Wiki

[1.x] 轻重链剖分

[1.1.x]前置概念

  • 重儿子:对于一个节点,它的儿子中最大的一个儿子为重儿子(子树最大的儿子)
  • 轻儿子:除了重儿子的其他儿子
  • 重边:连接重儿子的边
  • 轻边:除了重边的边
  • 重链:把1个轻儿子和相邻重儿子连起来的链,即一个重链总是以轻儿子或者根重儿子为头的重儿子串

[1.2.x]预处理

我们进行两次dfs,分别维护这些东西

[1.2.1.x]第一次dfs

第一次dfs主要是确定重儿子,为第二次dfs做准备,顺便处理可以一次性处理的东西。

  • dep[i]节点i的深度
  • fa[i]节点i的父亲
  • son[i]节点i的重儿子
  • size[i]节点i为根的子树大小
  1. inline void dfs1(int now,int father){
  2. size[now] = 1 , deep[now] = deep[father] + 1 , fa[now] = father;
  3. for(int i = head[now];i;i = next[i]){
  4. if(ver[i] == fa[now]) continue;
  5. dfs1(ver[i],now);
  6. size[now] += size[ ver[i] ];
  7. son[now] = size[ son[now] ] > size[ ver[i] ] ? son[now] : ver[i]; // 更新重儿子
  8. }
  9. return;
  10. }

[1.2.2.x]第二次dfs

第二次dfs是在根据已经得到的重儿子来确定一个特别的dfs序,来维护一些性质

这个dfs序号的特别要求是:先走重儿子,这样能保证重儿子链的dfs序是连续的

  • top[i]节点i所在的重链头
  • id[i]节点idfs序号
  • b[id[i]]通过节点idfs序索引a[i]
  1. inline void dfs2(int now,int toper){
  2. id[now] = ++id[0] , top[now] = toper , b[id[0]] = a[now];
  3. if(!son[now]) return; // 没有重儿子即叶子节点
  4. dfs2(son[now],toper); // 重儿子先走
  5. for(int i = head[now];i;i = next[i]){
  6. if(id[ ver[i] ]) continue;
  7. dfs2(ver[i],ver[i]);
  8. }
  9. return;
  10. }

[1.3.x]线段树维护

经过预处理后,我们可以发现,所有在同一个重链中的重儿子的dfs序是连续的,同时,还有一个性质,以i为根的子树上所有的点的dfs序都在[id[i],id[i]+size[i]-1]中。因此我们可以以dfs序为索引建立线段树

  1. int TOT;
  2. struct node{
  3. int l,r;
  4. ll sum,tag;
  5. int dis(){return r-l+1;}
  6. node * ls, * rs;
  7. }Tree[4*N];
  8. inline node * create(){return &Tree[++TOT];}
  9. inline void pushdown(node * cur){
  10. cur->ls->sum += cur->tag * cur->ls->dis(), cur->ls->sum %= p;
  11. cur->ls->tag += cur->tag , cur->ls->tag %= p;
  12. cur->rs->sum += cur->tag * cur->rs->dis(), cur->rs->sum %= p;
  13. cur->rs->tag += cur->tag , cur->rs->tag %= p;
  14. cur->tag = 0;
  15. return;
  16. }
  17. inline void pushup(node * cur){cur->sum = (cur->ls->sum + cur->rs->sum)%p;}
  18. inline void build(node * cur,int l,int r){
  19. cur->l = l , cur->r = r , cur->sum = cur->tag = 0;
  20. if(cur->l == cur->r){cur->sum = b[l];return;}
  21. int mid = (l+r)>>1;
  22. cur->ls = create() , cur->rs = create();
  23. build(cur->ls,l,mid) , build(cur->rs,mid+1,r);
  24. pushup(cur);
  25. return;
  26. }
  27. inline void edit(node * cur,int l,int r,int x){
  28. if(l > cur->r || r < cur->l) return;
  29. if(l <= cur->l && cur->r <= r){
  30. cur->sum += x * cur->dis() % p , cur->sum %= p , cur->tag += x;
  31. return;
  32. }
  33. if(cur->l == cur->r) return;
  34. pushdown(cur);
  35. if(l <= cur->ls->r) edit(cur->ls,l,r,x);
  36. if(r >= cur->rs->l) edit(cur->rs,l,r,x);
  37. pushup(cur);
  38. return;
  39. }
  40. inline ll query(node * cur,int l,int r){
  41. if(l > cur->r || r < cur->l) return 0;
  42. if(l <= cur->l && cur->r <= r) return cur->sum;
  43. pushdown(cur);
  44. return (query(cur->ls,l,r) + query(cur->rs,l,r))%p;
  45. }

[1.4.x]实现

[1.4.1.x]链上修改

首先这里肯定要用到线段树的区间修改,怎样增加效率呢。首先我们已经有了所有重链的序号连续,那自然通过top[i]不断向上跳,类似于树上倍增LCA,不断上跳直到两个点在同一个重链中。

  1. inline void edit_chain(node * cur){
  2. //把x到y都加上z
  3. while(top[x] != top[y]){
  4. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  5. edit(cur,id[top[x]],id[x],z);
  6. x = fa[ top[x] ];
  7. }
  8. if(id[x] > id[y]) std::swap(x,y);
  9. edit(cur,id[x],id[y],z);
  10. return;
  11. }

链上查询和链上修改同理。

  1. inline void query_chain(node * cur){
  2. //查询x到y的和
  3. ll ans = 0;
  4. while(top[x] != top[y]){
  5. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  6. ans += query(cur,id[top[x]],id[x]) , ans %= p;
  7. x = fa[top[x]];
  8. }
  9. if(id[x] > id[y]) std::swap(x,y);
  10. ans += query(cur,id[x],id[y]) , ans %= p;
  11. cout << ans << "\n";
  12. return;
  13. }

树上修改和查询就更简单了,直接操作[id[i],id[i]+size[i]-1]就行

  1. inline void edit_tree(node * cur){
  2. //给x的子树都加上z
  3. edit(cur,id[x],id[x]+size[x]-1,z);
  4. return;
  5. }
  6. inline void query_tree(node * cur){
  7. //查询x的子树和
  8. cout << query(cur,id[x],id[x]+size[x]-1) << "\n";
  9. return;
  10. }

区间最值的维护和区间和的维护基本上类似,可以参考相关题目第二题的代码。

[1.5.x]相关题目

P3384 【模板】轻重链剖分

  1. #include<map>
  2. #include<set>
  3. #include<cmath>
  4. #include<queue>
  5. #include<bitset>
  6. #include<vector>
  7. #include<cstdio>
  8. #include<cstring>
  9. #include<cstdlib>
  10. #include<iostream>
  11. #include<algorithm>
  12. #define rep(i,a,b) for(register int i = (a);i <= (b);++i)
  13. #define per(i,a,b) for(register int i = (a);i >= (b);--i)
  14. typedef long long ll;
  15. typedef unsigned long long ull;
  16. using std::string;using std::cin;using std::cout;
  17. const int N = 1e5+5;
  18. int n,m,root,p,a[2*N],b[2*N],x,y,z,opt;
  19. int head[2*N],next[2*N],ver[2*N],tot;
  20. int fa[2*N],top[2*N],deep[2*N],son[2*N],size[2*N],id[2*N];
  21. //------------------------------------------------------
  22. inline void link(int x,int y){ver[++tot] = y , next[tot] = head[x] , head[x] = tot;}
  23. inline void dfs1(int now,int father){
  24. size[now] = 1 , deep[now] = deep[father] + 1 , fa[now] = father;
  25. for(int i = head[now];i;i = next[i]){
  26. if(ver[i] == fa[now]) continue;
  27. dfs1(ver[i],now);
  28. size[now] += size[ ver[i] ];
  29. son[now] = size[ son[now] ] > size[ ver[i] ] ? son[now] : ver[i]; // 更新重儿子
  30. }
  31. return;
  32. }
  33. inline void dfs2(int now,int toper){
  34. id[now] = ++id[0] , top[now] = toper , b[id[0]] = a[now];
  35. if(!son[now]) return; // 没有重儿子即叶子节点
  36. dfs2(son[now],toper); // 重儿子先走
  37. for(int i = head[now];i;i = next[i]){
  38. if(id[ ver[i] ]) continue;
  39. dfs2(ver[i],ver[i]);
  40. }
  41. return;
  42. }
  43. //------------------------------------------------------
  44. int TOT;
  45. struct node{
  46. int l,r;
  47. ll sum,tag;
  48. int dis(){return r-l+1;}
  49. node * ls, * rs;
  50. }Tree[4*N];
  51. inline node * create(){return &Tree[++TOT];}
  52. inline void pushdown(node * cur){
  53. cur->ls->sum += cur->tag * cur->ls->dis(), cur->ls->sum %= p;
  54. cur->ls->tag += cur->tag , cur->ls->tag %= p;
  55. cur->rs->sum += cur->tag * cur->rs->dis(), cur->rs->sum %= p;
  56. cur->rs->tag += cur->tag , cur->rs->tag %= p;
  57. cur->tag = 0;
  58. return;
  59. }
  60. inline void pushup(node * cur){cur->sum = (cur->ls->sum + cur->rs->sum)%p;}
  61. inline void build(node * cur,int l,int r){
  62. cur->l = l , cur->r = r , cur->sum = cur->tag = 0;
  63. if(cur->l == cur->r){cur->sum = b[l];return;}
  64. int mid = (l+r)>>1;
  65. cur->ls = create() , cur->rs = create();
  66. build(cur->ls,l,mid) , build(cur->rs,mid+1,r);
  67. pushup(cur);
  68. return;
  69. }
  70. inline void edit(node * cur,int l,int r,int x){
  71. if(l > cur->r || r < cur->l) return;
  72. if(l <= cur->l && cur->r <= r){
  73. cur->sum += x * cur->dis() % p , cur->sum %= p , cur->tag += x;
  74. return;
  75. }
  76. if(cur->l == cur->r) return;
  77. pushdown(cur);
  78. if(l <= cur->ls->r) edit(cur->ls,l,r,x);
  79. if(r >= cur->rs->l) edit(cur->rs,l,r,x);
  80. pushup(cur);
  81. return;
  82. }
  83. inline ll query(node * cur,int l,int r){
  84. if(l > cur->r || r < cur->l) return 0;
  85. if(l <= cur->l && cur->r <= r) return cur->sum;
  86. pushdown(cur);
  87. return (query(cur->ls,l,r) + query(cur->rs,l,r))%p;
  88. }
  89. //------------------------------------------------------
  90. inline void edit_chain(node * cur){
  91. //把x到y都加上z
  92. while(top[x] != top[y]){
  93. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  94. edit(cur,id[top[x]],id[x],z);
  95. x = fa[ top[x] ];
  96. }
  97. if(id[x] > id[y]) std::swap(x,y);
  98. edit(cur,id[x],id[y],z);
  99. return;
  100. }
  101. inline void query_chain(node * cur){
  102. //查询x到y的和
  103. ll ans = 0;
  104. while(top[x] != top[y]){
  105. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  106. ans += query(cur,id[top[x]],id[x]) , ans %= p;
  107. x = fa[top[x]];
  108. }
  109. if(id[x] > id[y]) std::swap(x,y);
  110. ans += query(cur,id[x],id[y]) , ans %= p;
  111. cout << ans << "\n";
  112. return;
  113. }
  114. inline void edit_tree(node * cur){
  115. //给x的子树都加上z
  116. edit(cur,id[x],id[x]+size[x]-1,z);
  117. return;
  118. }
  119. inline void query_tree(node * cur){
  120. //查询x的子树和
  121. cout << query(cur,id[x],id[x]+size[x]-1) << "\n";
  122. return;
  123. }
  124. //------------------------------------------------------
  125. int main(){
  126. std::ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  127. //freopen("in.in", "r", stdin);
  128. cin >> n >> m >> root >> p;
  129. rep(i,1,n) cin >> a[i];
  130. rep(i,1,n-1){
  131. cin >> x >> y;
  132. link(x,y) , link(y,x);
  133. }
  134. fa[root] = 0 , deep[fa[root]] = 0;
  135. dfs1(root,0);
  136. dfs2(root,root);
  137. node * Root = create();
  138. build(Root,1,n);
  139. while(m--){
  140. cin >> opt;
  141. if(opt == 1){//链改
  142. cin >> x >> y >> z;
  143. z %= p;
  144. edit_chain(Root);
  145. } else if(opt == 2){//链查
  146. cin >> x >> y;
  147. query_chain(Root);
  148. } else if(opt == 3){//树改
  149. cin >> x >> z;
  150. z %= p;
  151. edit_tree(Root);
  152. } else if(opt == 4){//树查
  153. cin >> x;
  154. query_tree(Root);
  155. }
  156. }
  157. return 0;
  158. }

P2590 [ZJOI2008]树的统计

  1. #include<map>
  2. #include<set>
  3. #include<cmath>
  4. #include<queue>
  5. #include<bitset>
  6. #include<vector>
  7. #include<cstdio>
  8. #include<cstring>
  9. #include<cstdlib>
  10. #include<iostream>
  11. #include<algorithm>
  12. #define rep(i,a,b) for(register int i = (a);i <= (b);++i)
  13. #define per(i,a,b) for(register int i = (a);i >= (b);--i)
  14. #define mkp std::make_pair
  15. typedef long long ll;
  16. typedef unsigned long long ull;
  17. using std::string;using std::cin;using std::cout;
  18. inline bool cmp(int x,int y){return x < y;}
  19. const int inf = 1e9+9;
  20. const int N = 1e6+9;
  21. const double eps = 1e-7;
  22. int _,n,m,a[2*N],u,v;
  23. string str;
  24. //----------------------------
  25. int head[2*N],next[2*N],ver[2*N],tot;
  26. inline void link(int x,int y){ver[++tot] = y , next[tot] = head[x] , head[x] = tot;}
  27. //----------------------------
  28. int fa[2*N],top[2*N],son[2*N],size[2*N],deep[2*N],id[2*N],b[2*N];
  29. inline void dfs1(int now,int fathter){
  30. fa[now] = fathter , deep[now] = deep[fathter] + 1 , size[now] = 1;
  31. for(int i = head[now];i;i = next[i]){
  32. if(ver[i] == fathter) continue;
  33. dfs1(ver[i],now);
  34. size[now] += size[ ver[i] ];
  35. son[now] = size[ son[now] ] > size[ son[ver[i]] ] ? son[now] : ver[i];
  36. }
  37. return;
  38. }
  39. inline void dfs2(int now,int toper){
  40. id[now] = ++id[0] , b[ id[0] ] = a[now] , top[now] = toper;
  41. if(!son[now]) return;
  42. dfs2(son[now],toper);
  43. for(int i = head[now];i;i = next[i]){
  44. if(id[ ver[i] ]) continue;
  45. dfs2(ver[i],ver[i]);
  46. }
  47. return;
  48. }
  49. //----------------------------
  50. int TOT;
  51. struct node{
  52. int l,r,sum,max;
  53. node * ls , * rs;
  54. }Tree[4*N];
  55. inline node * create(){return &Tree[++TOT];}
  56. inline void pushup(node * cur){cur->sum = cur->ls->sum + cur->rs->sum , cur->max = std::max(cur->ls->max,cur->rs->max);}
  57. inline void build(node * cur,int l,int r){
  58. cur->l = l , cur->r = r , cur->sum = cur->max = 0;
  59. if(l == r){
  60. cur->sum = cur->max = b[l];
  61. return;
  62. }
  63. int mid = (l+r) >> 1;
  64. cur->ls = create() , cur->rs = create();
  65. build(cur->ls,l,mid) , build(cur->rs,mid+1,r);
  66. pushup(cur);
  67. return;
  68. }
  69. inline void edit(node * cur){
  70. if(cur->l == cur->r){
  71. cur->sum = cur->max = v;
  72. return;
  73. }
  74. if(u <= cur->ls->r) edit(cur->ls);
  75. if(cur->rs->l <= u) edit(cur->rs);
  76. pushup(cur);
  77. return;
  78. }
  79. inline int query_max(node * cur){
  80. if(cur->r < u || v < cur->l) return -inf;
  81. if(u <= cur->l && cur->r <= v) return cur->max;
  82. return std::max(query_max(cur->ls),query_max(cur->rs));
  83. }
  84. inline int query_sum(node * cur){
  85. if(cur->r < u || v < cur->l) return 0;
  86. if(u <= cur->l && cur->r <= v) return cur->sum;
  87. return query_sum(cur->ls) + query_sum(cur->rs);
  88. }
  89. //----------------------------
  90. int main(){
  91. std::ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  92. //freopen("in.in", "r", stdin);
  93. cin >> n;
  94. rep(i,1,n-1){
  95. cin >> u >> v;
  96. link(u,v) , link(v,u);
  97. }
  98. rep(i,1,n) cin >> a[i];
  99. //----------------------------
  100. dfs1(1,1) , dfs2(1,1);
  101. node * root = create();
  102. build(root,1,n);
  103. //----------------------------
  104. cin >> m;
  105. while(m--){
  106. cin >> str >> u >> v;
  107. if(str == "CHANGE"){
  108. u = id[u];
  109. edit(root);
  110. } else if(str == "QMAX"){
  111. int x = u , y = v , ans = -inf;
  112. while(top[x] != top[y]){
  113. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  114. u = id[ top[x] ] , v = id[x] , x = fa[ top[x] ] , ans = std::max(ans,query_max(root));
  115. }
  116. if(id[x] > id[y]) std::swap(x,y);
  117. u = id[x] , v = id[y] , ans = std::max(ans,query_max(root));
  118. cout << ans << "\n";
  119. } else if(str == "QSUM"){
  120. int x = u , y = v , ans = 0;
  121. while(top[x] != top[y]){
  122. if(deep[ top[x] ] < deep[ top[y] ]) std::swap(x,y);
  123. u = id[ top[x] ] , v = id[x] , x = fa[ top[x] ] , ans += query_sum(root);
  124. }
  125. if(id[x] > id[y]) std::swap(x,y);
  126. u = id[x] , v = id[y] , ans += query_sum(root);
  127. cout << ans << "\n";
  128. }
  129. }
  130. return 0;
  131. }

P3178 [HAOI2015]树上操作

  1. #include<map>
  2. #include<set>
  3. #include<cmath>
  4. #include<queue>
  5. #include<bitset>
  6. #include<vector>
  7. #include<cstdio>
  8. #include<cstring>
  9. #include<cstdlib>
  10. #include<iostream>
  11. #include<algorithm>
  12. #define rep(i,a,b) for(register int i = (a);i <= (b);++i)
  13. #define per(i,a,b) for(register int i = (a);i >= (b);--i)
  14. #define mkp std::make_pair
  15. typedef long long ll;
  16. typedef unsigned long long ull;
  17. using std::string;using std::cin;using std::cout;
  18. inline bool cmp(int x,int y){return x < y;}
  19. const int N = 1e5+9;
  20. const int inf = 1e9+9;
  21. const double eps = 1e-7;
  22. int _,n,m,a[2*N],opt,u,v,w;
  23. //----------------------------
  24. int head[2*N],ver[2*N],next[2*N],tot;
  25. inline void link(int x,int y){ver[++tot] = y , next[tot] = head[x] , head[x] = tot;}
  26. //----------------------------
  27. int fa[2*N],top[2*N],deep[2*N],size[2*N],son[2*N],id[2*N],b[2*N];
  28. inline void dfs1(int now,int father){
  29. fa[now] = father , deep[now] = deep[father] + 1 , size[now] = 1;
  30. for(int i = head[now];i;i = next[i]){
  31. if(ver[i] == father) continue;
  32. dfs1(ver[i],now);
  33. size[now] += size[ ver[i] ];
  34. son[now] = size[ son[now] ] > size[ ver[i] ] ? son[now] : ver[i];
  35. }
  36. }
  37. inline void dfs2(int now,int toper){
  38. top[now] = toper , id[now] = ++id[0] , b[ id[0] ] = a[now];
  39. if(!son[now]) return;
  40. dfs2(son[now],toper);
  41. for(int i = head[now];i;i = next[i]){
  42. if(id[ ver[i] ]) continue;
  43. dfs2(ver[i],ver[i]);
  44. }
  45. }
  46. //----------------------------
  47. int TOT;
  48. struct node{
  49. int l,r;
  50. ll tag,sum;
  51. node * ls , * rs;
  52. ll dis(){return r-l+1;}
  53. }Tree[4*N];
  54. inline node * create(){return &Tree[++TOT];}
  55. inline void pushup(node * cur){cur->sum = cur->ls->sum + cur->rs->sum;}
  56. inline void pushdown(node * cur){
  57. cur->ls->tag += cur->tag , cur->rs->tag += cur->tag;
  58. cur->ls->sum += cur->tag * cur->ls->dis();
  59. cur->rs->sum += cur->tag * cur->rs->dis();
  60. cur->tag = 0;
  61. return;
  62. }
  63. inline void build(node * cur,int l,int r){
  64. cur->l = l , cur->r = r , cur->tag = 0;
  65. if(l == r){cur->sum = b[l];return;}
  66. int mid = (l+r) >> 1;
  67. cur->ls = create() , cur->rs = create();
  68. build(cur->ls,l,mid) , build(cur->rs,mid+1,r);
  69. pushup(cur);
  70. }
  71. inline void edit(node * cur){
  72. if(v < cur->l || cur->r < u) return;
  73. if(u <= cur->l && cur->r <= v){
  74. cur->tag += w , cur->sum += w * cur->dis();
  75. return;
  76. }
  77. if(cur->l == cur->r) return;
  78. pushdown(cur);
  79. if(u <= cur->ls->r) edit(cur->ls);
  80. if(cur->rs->l <= v) edit(cur->rs);
  81. pushup(cur);
  82. }
  83. inline ll query(node * cur){
  84. if(v < cur->l || cur->r < u) return 0;
  85. if(u <= cur->l && cur->r <= v) return cur->sum;
  86. pushdown(cur);
  87. return query(cur->ls) + query(cur->rs);
  88. }
  89. //----------------------------
  90. int main(){
  91. std::ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  92. cin >> n >> m;
  93. rep(i,1,n) cin >> a[i];
  94. rep(i,1,n-1){
  95. cin >> u >> v;
  96. link(u,v) , link(v,u);
  97. }
  98. dfs1(1,1) , dfs2(1,1);
  99. node * root = create();
  100. build(root,1,n);
  101. int p,x;
  102. while(m--){
  103. cin >> opt;
  104. if(opt == 1){//point edit
  105. cin >> p >> x;
  106. u = v = id[p] , w = x;
  107. edit(root);
  108. } else if(opt == 2){//tree edit
  109. cin >> p >> x;
  110. u = id[p] , v = id[p] + size[p] - 1 , w = x;
  111. edit(root);
  112. } else {//query
  113. cin >> p;
  114. ll ans = 0;
  115. while(top[1] != top[p]){
  116. u = id[ top[p] ] , v = id[p];
  117. ans += query(root);
  118. p = fa[ top[p] ];
  119. }
  120. u = id[1] , v = id[p];
  121. cout << ans + query(root) << "\n";
  122. }
  123. }
  124. return 0;
  125. }