简介

  树链剖分通常用来解决一类维护静态树上路径信息的问题, 例如:
给定一棵点带权树, 接下来每次操作会修改某条路径上所有点的权值(修改为同一个值或是同加上一个值等) , 以及询问某条路径上所有点的权值和。
当这棵树是一条链时, 这个问题实际上就是一个序列上区间修改、 区间询问的问题, 可以用之前介绍的几个数据结构解决。
对于其他情况, 由于树的形态是不变的, 因此树链剖分的策略是将这些点按某种方式组织起来, 剖分成为若干条链, 每条链就相当于一个序列, 则操作路径可以拆分为剖分好的某几条链, 也就是若干个完整序列或是某个序列上的一段区间, 此时再利用线段树等处理序列上区间操作问题的数据结构来解决。
树链剖分的核心就是如何恰当的剖分树为若干条链。 当链的划分方式确定后, 我们只要将它们看做是一个个序列, 将所有序列按顺序拼接起来后, 每条链就成为了一段区间, 而序列上的区间问题是我们所熟悉和擅长解决的。

方法

轻重链剖分

我们将树中的边分成两种: 轻边, 重边。 如下图中加粗的边是重边, 其余是轻边。

我们可以以任意点为根, 然后记 size(u) 为以 u 为根的子树的结点个数, 令 v 为u 所有儿子中 size 值最大的一个儿子, 则(u,v) 为重边, v 称为u 的重儿子。 u 到其余儿子的边为
轻边。

树链剖分求LCA

  传送门

例题

【浙江省选2008】树的统计

题目背景

ZJOI2008 DAY1 T4

题目描述

一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w 。
我们将以下面的形式来要求你对这棵树完成一些操作:
I.CHANGE u t :把结点 u 的权值改为 t ;
II.QMAX u v :询问从点 u 到点 v 的路径上的节点的最大权值;
III.QSUM u v :询问从点 u 到点 v 的路径上的节点的权值和。

注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身。

输入格式

输入第一行为一个整数 n ,表示节点的个数。
接下来 n–1 行,每行 2 个整数 a 和 b ,表示节点 a 和节点 b 之间有一条边相连。
接下来 n 行,每行一个整数,第 i 行的整数 wi 表示节点 i 的权值。
接下来 1 行,为一个整数 q ,表示操作的总数。
接下来 q 行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

输出格式

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

样例数据 1

输入


1 2 
2 3 
4 1 
4 2 1 3 
12 
QMAX 3 4 
QMAX 3 3 
QMAX 3 2 
QMAX 2 3 
QSUM 3 4 
QSUM 2 1 
CHANGE 1 5 
QMAX 3 4 
CHANGE 3 6 
QMAX 3 4 
QMAX 2 4 
QSUM 3 4

输出





10 




16

备注

【数据范围】

对于 100% 的数据,保证1<=n<=30000;0<=q<=200000;中途操作中保证每个节点的权值 w 在 -30000 到 30000 之间。

【题目分析】

模板题

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<string>
  6. #include<algorithm>
  7. #include<cmath>
  8. using namespace std;
  9.  
  10. const int N = ;
  11. const int oo = 0x3f3f3f3f;
  12.  
  13. int dep[N], sze[N], top[N], son[N], pos[N], idx[N], val[N], fa[N];
  14. int ecnt, adj[N], go[N << ], nxt[N << ], tot;
  15. int sum[N * ], maxx[N * ];
  16. int n, q;
  17.  
  18. inline int Re(){
  19. int i = , f = ; char ch = getchar();
  20. for(; (ch < '' || ch > '') && ch != '-'; ch = getchar());
  21. if(ch == '-') f = -, ch = getchar();
  22. for(; ch >= '' && ch <= ''; ch = getchar())
  23. i = (i << ) + (i << ) + (ch - '');
  24. return i * f;
  25. }
  26.  
  27. inline void Wr(int x){
  28. if(x < ) putchar('-'), x = -x;
  29. if(x > ) Wr(x / );
  30. putchar(x % + '');
  31. }
  32.  
  33. inline void addEdge(const int &u, const int &v){
  34. nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
  35. nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
  36. }
  37.  
  38. inline void dfs1(const int &u, const int &f){
  39. dep[u] = dep[f] + ;
  40. fa[u] = f;
  41. sze[u] = ;
  42. for(int e = adj[u]; e; e = nxt[e]){
  43. int v = go[e];
  44. if(v == f) continue;
  45. dfs1(v, u);
  46. sze[u] += sze[v];
  47. if(sze[v] > sze[son[u]]) son[u] = v;
  48. }
  49. }
  50.  
  51. inline void dfs2(const int &u, const int &f){
  52. if(son[u]){ //先查重儿子, 保证重链连续
  53. top[son[u]] = top[u];
  54. idx[pos[son[u]] = ++tot] = son[u];
  55. dfs2(son[u], u);
  56. }
  57. for(int e = adj[u]; e; e = nxt[e]){
  58. int v = go[e];
  59. if(v == f || v == son[u]) continue;
  60. top[v] = v;
  61. idx[pos[v] = ++tot] = v;
  62. dfs2(v, u);
  63. }
  64. }
  65.  
  66. inline int chkMax(const int &x, const int &y){
  67. if(x > y) return x;
  68. return y;
  69. }
  70.  
  71. inline void build(int k, int l, int r){
  72. if(l == r){
  73. sum[k] = maxx[k] = val[idx[l]];
  74. return;
  75. }
  76. int mid = l + r >> , lc = k << , rc = k << | ;
  77. build(lc, l, mid);
  78. build(rc, mid + , r);
  79. sum[k] = sum[lc] + sum[rc];
  80. maxx[k] = chkMax(maxx[lc], maxx[rc]);
  81. }
  82.  
  83. inline int PathSum(int k, int l, int r, int x, int y){
  84. if(x <= l && r <= y) return sum[k];
  85. int mid = l + r >> , lc = k << , rc = k << | ;
  86. int ret = ;
  87. if(x <= mid) ret += PathSum(lc, l, mid, x, y);
  88. if(y > mid) ret += PathSum(rc, mid + , r, x, y);
  89. return ret;
  90. }
  91.  
  92. inline int PathMax(int k, int l, int r, int x, int y){
  93. if(x <= l && r <= y) return maxx[k];
  94. int mid = l + r >> , lc = k << , rc = k << | ;
  95. int ret = -oo;
  96. if(x <= mid) ret = chkMax(ret, PathMax(lc, l, mid, x, y));
  97. if(y > mid) ret = chkMax(ret, PathMax(rc, mid + , r, x, y));
  98. return ret;
  99. }
  100.  
  101. inline void PrintSum(int u, int v){
  102. int ret = ;
  103. while(top[u] != top[v]){
  104. if(dep[top[u]] < dep[top[v]]) swap(u, v);
  105. ret += PathSum(, , n, pos[top[u]], pos[u]);
  106. u = fa[top[u]];
  107. }
  108. if(dep[u] > dep[v]) swap(u, v);
  109. ret += PathSum(, , n, pos[u], pos[v]);
  110. Wr(ret), putchar('\n');
  111. }
  112.  
  113. inline void PrintMax(int u, int v){
  114. int ret = -oo;
  115. while(top[u] != top[v]){
  116. if(dep[top[u]] < dep[top[v]]) swap(u, v);
  117. ret = chkMax(ret, PathMax(, , n, pos[top[u]], pos[u]));
  118. u = fa[top[u]];
  119. }
  120. if(dep[u] > dep[v]) swap(u, v);
  121. ret = chkMax(ret, PathMax(, , n, pos[u], pos[v]));
  122. Wr(ret), putchar('\n');
  123. }
  124.  
  125. inline void modify(int k, int l, int r, int pos, int v){
  126. if(l == r){
  127. sum[k] = v;
  128. maxx[k] = v;
  129. return;
  130. }
  131. int mid = l + r >> , lc = k << , rc = k << | ;
  132. if(pos <= mid) modify(lc, l, mid, pos, v);
  133. else modify(rc, mid + , r, pos, v);
  134. sum[k] = sum[lc] + sum[rc];
  135. maxx[k] = chkMax(maxx[lc], maxx[rc]);
  136. }
  137.  
  138. inline void print(int k){
  139. if(k == ) return;
  140. print(k<<);print(k<<|);
  141. cout<<sum[k]<<" "<<maxx[k]<<endl;
  142. }
  143.  
  144. int main(){
  145. // freopen("h.in", "r", stdin);
  146. n = Re();
  147. for(int i = ; i < n; i++){
  148. int a = Re(), b = Re();
  149. addEdge(a, b);
  150. }
  151. for(int i = ; i <= n; i++) val[i] = Re();
  152.  
  153. dep[] = -, top[] = , idx[] = , pos[] = , tot = ;
  154. dfs1(, );
  155. dfs2(, );
  156. build(, , n);
  157. // print(1);
  158.  
  159. q = Re();
  160. for(int i = ; i <= q; i++){
  161. char opt[]; int u, v, t;
  162. scanf("%s", opt + );
  163. if(opt[] == 'H'){ //change
  164. u = Re(), t = Re();
  165. modify(, , n, pos[u], t);
  166. }
  167. else if(opt[] == 'M'){ //qmax
  168. u = Re(), v = Re();
  169. PrintMax(u, v);
  170. }
  171. else{ //qsum
  172. u = Re(), v = Re();
  173. PrintSum(u, v);
  174. }
  175. }
  176. }

【bzoj2243】【山东省选2011】染色

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数x和y,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

Output

对于每个询问操作,输出一行答案。

Sample Input

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output

3
1
2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

【题目分析】

 

 树链剖分,维护节点的颜色段数, 修改标记, 左端、右端颜色, 注意用左右子树更新根节点时颜色相同要-1, 数组线段树不好维护可以写成结构体!!

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<string>
  6. #include<algorithm>
  7. #include<cmath>
  8. #include<vector>
  9. using namespace std;
  10.  
  11. const int N = 3e5;
  12.  
  13. int n, m;
  14. int sze[N], dep[N], val[N], idx[N], pos[N], fa[N], top[N], son[N], tot;
  15. int ecnt, adj[N], go[N << ], nxt[N << ];
  16.  
  17. inline int Re(){
  18. int i = , f = ; char ch = getchar();
  19. for(; (ch < '' || ch > '') && ch != '-'; ch = getchar());
  20. if(ch == '-') f = -, ch = getchar();
  21. for(; ch >= '' && ch <= ''; ch = getchar())
  22. i = (i << ) + (i << ) + (ch - '');
  23. return i * f;
  24. }
  25.  
  26. inline void Wr(int x){
  27. if(x < ) putchar('-'), x = -x;
  28. if(x > ) Wr(x / );
  29. putchar(x % + '');
  30. }
  31.  
  32. inline void addEdge(const int &u, const int &v){
  33. nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
  34. nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
  35. }
  36.  
  37. inline void dfs1(const int &u, const int &f){
  38. dep[u] = dep[f] + ;
  39. sze[u] = ;
  40. fa[u] = f;
  41. for(int e = adj[u]; e; e = nxt[e]){
  42. int v = go[e];
  43. if(v == f) continue;
  44. dfs1(v, u);
  45. sze[u] += sze[v];
  46. if(sze[v] > sze[son[u]]) son[u] = v;
  47. }
  48. }
  49.  
  50. inline void dfs2(const int &u, const int &f){
  51. if(son[u]){
  52. top[son[u]] = top[u];
  53. idx[pos[son[u]] = ++tot] = son[u];
  54. dfs2(son[u], u);
  55. }
  56. for(int e = adj[u]; e; e = nxt[e]){
  57. int v = go[e];
  58. if(v == f || v == son[u]) continue;
  59. top[v] = v;
  60. idx[pos[v] = ++tot] = v;
  61. dfs2(v, u);
  62. }
  63. }
  64.  
  65. struct node{
  66. int cnt, lcol, rcol, tag;
  67. node():cnt(), lcol(-), rcol(-), tag(-){}
  68. };
  69.  
  70. namespace SegTree{
  71. node tr[N * ];
  72. inline void upt(int k){
  73. int lc = k << , rc = k << | ;
  74. tr[k].lcol = tr[lc].lcol;
  75. tr[k].rcol = tr[rc].rcol;
  76. tr[k].cnt = tr[lc].cnt + tr[rc].cnt - (tr[lc].rcol == tr[rc].lcol);
  77. }
  78. inline void cover(int k, int v){
  79. tr[k].lcol = tr[k].rcol = v;
  80. tr[k].cnt = ;
  81. tr[k].tag = v;
  82. }
  83. inline void pushDown(int k){
  84. int lc = k << , rc = k << | ;
  85. if(tr[k].tag != -){
  86. cover(lc, tr[k].tag);
  87. cover(rc, tr[k].tag);
  88. tr[k].cnt = , tr[k].lcol = tr[k].rcol = tr[k].tag;
  89. tr[k].tag = -;
  90. }
  91. }
  92.  
  93. inline void build(int k, int l, int r){
  94. if(l == r){
  95. tr[k].cnt = ;
  96. tr[k].tag = -;
  97. tr[k].lcol = tr[k].rcol = val[idx[l]];
  98. return;
  99. }
  100. int mid = l + r >> , lc = k << , rc = k << | ;
  101. build(lc, l, mid);
  102. build(rc, mid + , r);
  103. upt(k);
  104. }
  105.  
  106. inline void modify(int k, int l, int r, int x, int y, int v){
  107. if(x <= l && r <= y){
  108. cover(k, v);
  109. return;
  110. }
  111. pushDown(k);
  112. int mid = l + r >> , lc = k << , rc = k << | ;
  113. if(x <= mid) modify(lc, l, mid, x, y, v);
  114. if(y > mid) modify(rc, mid + , r, x, y, v);
  115. upt(k);
  116. }
  117.  
  118. inline node query(int k, int l, int r, int x, int y){
  119. if(l == x && r == y) return tr[k];
  120. pushDown(k);
  121. int mid = l + r >> , lc = k << , rc = k << | ;
  122. if(y <= mid) return query(lc, l, mid, x, y);
  123. else if(x > mid) return query(rc, mid + , r, x, y);
  124. else {
  125. node ret, ret1, ret2;
  126. ret1 = query(lc, l, mid, x, mid);
  127. ret2 = query(rc, mid + , r, mid + , y);
  128. ret.cnt = ret1.cnt + ret2.cnt - (ret1.rcol == ret2.lcol);
  129. ret.lcol = ret1.lcol, ret.rcol = ret2.rcol;
  130. return ret;
  131. }
  132. // cout<<ret1.lcol<<" "<<ret1.lcol<<" "<<ret2.lcol<<" "<<ret2.rcol<<endl;
  133.  
  134. }
  135. }using namespace SegTree;
  136. inline void PrintCnt(int a, int b){
  137. int ans = , acol = -, bcol = -;
  138. while(top[a] != top[b]){
  139. if(dep[top[a]] < dep[top[b]]) swap(a, b), swap(acol, bcol);
  140. node ret = query(, , n, pos[top[a]], pos[a]);
  141. ans += ret.cnt;
  142. if(ret.rcol == acol) ans--;
  143. a = fa[top[a]], acol = ret.lcol;
  144. }
  145. if(dep[a] > dep[b]) swap(a, b), swap(acol, bcol);
  146. node ret = query(, , n, pos[a], pos[b]);
  147. ans += ret.cnt - (ret.lcol == acol) - (ret.rcol == bcol);
  148. Wr(ans);
  149. }
  150.  
  151. inline void PathModify(int a, int b, int v){
  152. while(top[a] != top[b]){
  153. if(dep[top[a]] < dep[top[b]]) swap(a, b);
  154. modify(, , n, pos[top[a]], pos[a], v);
  155. a = fa[top[a]];
  156. }
  157. if(dep[a] > dep[b]) swap(a, b);
  158. modify(, , n, pos[a], pos[b], v);
  159. }
  160.  
  161. int main(){
  162. freopen("h.in", "r", stdin);
  163. n = Re(), m = Re();
  164. for(int i = ; i <= n; i++) val[i] = Re();
  165. for(int i = ; i < n; i++){
  166. int a = Re(), b = Re();
  167. addEdge(a, b);
  168. }
  169. dep[] = -, top[] = pos[] = idx[] = tot = ;
  170. dfs1(, );
  171. dfs2(, );
  172. build(, , n);
  173. for(int i = ; i <= m; i++){
  174. char opt; opt = getchar();
  175. while(opt != 'Q' && opt != 'C') opt = getchar();
  176. int a, b, c;
  177. if(opt == 'C'){
  178. a = Re(), b = Re(), c = Re();
  179. PathModify(a, b, c);
  180. }
  181. else if(opt == 'Q'){
  182. a = Re(), b = Re();
  183. PrintCnt(a, b), putchar('\n');
  184. }
  185. }
  186. return ;
  187. }

【NOI复习】树链剖分的更多相关文章

  1. 算法复习——树链剖分模板(bzoj1036)

    题目: 题目背景 ZJOI2008 DAY1 T4 题目描述 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w .我们将以下面的形式来要求你对这棵树完成一些操作:I.CHAN ...

  2. [BZOJ2243][SDOI2011]染色 解题报告|树链剖分

    Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“ ...

  3. 7.18 NOI模拟赛 树论 线段树 树链剖分 树的直径的中心 SG函数 换根

    LINK:树论 不愧是我认识的出题人 出的题就是牛掰 == 他好像不认识我 考试的时候 只会写42 还有两个subtask写挂了 拿了37 确实两个subtask合起来只有5分的好成绩 父亲能转移到自 ...

  4. 【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)

    题面 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ l ≤ r ≤ n , 1 ≤ d ≤ 1 0 8 2 ≤ n ≤ 2 × 10^5,1 ≤ m ≤ 2 ...

  5. BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )

    s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...

  6. 【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

    题解: 好像和noi那题并没有什么区别 只是加上了修改和变成树上 比较显然我们可以用树链剖分来维护

  7. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...

  8. bzoj1036 [ZJOI2008]树的统计Count(树链剖分)

    Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. Q ...

  9. BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

    http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题 ...

随机推荐

  1. css代码初始化

    @charset "utf-8";/* 页面元素初始化和常用样式定义-start *//*======== 全局 ========*/body, div, dl, dt, dd, ...

  2. oracle 11g 完全卸载方法

    网上好多卸载教程都前篇一律,但很多卸完重装都有问题,卸了几次装了几次,就特地总结整理一下 另外说一句:在完全删除(或者叫卸载)oracle时,没有必要特别意oracle提示问题,只要把oracle痕迹 ...

  3. LANMP一键安装包 版本服务任你选 可安装单一服务

    介绍与使用 更多内容请到 乌龟运维 wuguiyunwei.com 请保证在系统原有yum源文件存在的情况下运行此脚本 以下以centos7.3为例: 下面以安装LNMP为例: ? 1 wget ht ...

  4. WPF的一些感悟

    第一天在博客园写东西,只写一些自己对WPF稚嫩的理解和感悟. 1.Code Snippet代码简写工具 可以创建自己的代码模板管理器——>>>工具菜单,代码片段管理器 考出现有的+更 ...

  5. PHP中递归最详解释.

    说到递归函数想必会有很多同学感到晕晕的,很难绕,容易绕错,那下面就让我来为大家详解一下. 首先,什么是递归函数呢? 1.所谓递归:指的是在函数内部,调用函数自身的操作.2.递归分两布:递(从最外层函数 ...

  6. dubbo 请求调用过程分析

    服务消费方发起请求 当服务的消费方引用了某远程服务,服务的应用方在spring的配置实例如下: <dubbo:referenceid="demoService"interfa ...

  7. URL重定向

    /** * URL重定向 * @param string $url 重定向的URL地址 * @param integer $time 重定向的等待时间(秒) * @param string $msg ...

  8. 框架基础:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码

    距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下.一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照.当然,也把自己晒的黑漆马虎的.不过这一段时间 ...

  9. pdf文件之itextpdf插入html内容以及中文解决方案

    简述 目前网上已经有很多种html文件直接转pdf的技术帖子,但是很少有直接将部分html作为段落插入到pdf中,而且也没有一个可以很好的解决中文显示的问题. 因此今天上午围绕这个问题进行了研究,把解 ...

  10. h5 新增的invalid事件,貌似有很大bug

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...