[关闭]
@WangYixu 2018-06-19T12:56:54.000000Z 字数 4822 阅读 943

树链剖分

OI 算法 笔记 树剖 线段树


为什么要树链剖分?

因为有些sb棒极了的出题人,一想到某个操作满足可加性,就开始考各种数据结构来维护这个操作,其中一种考法就是在一棵树上,每个点都有权值,来维护这棵树上的某个链或子树的权值“和”,还要支持什么子树修改啊,链修改啊之类的蛋疼必要操作,为了卡过出题人的毒瘤数据提高我们算法的效率,我们引入树链剖分这一操作。

什么是树链剖分?

这里用加法代表那些满足可加性的操作(还有乘法、异或等等)。

树链剖分,就是将一棵树剖分成几条链,变成线性结构,然后就可以跑各种线段树维护了。

为了让树上的链在线性结构上尽量分成较少的链,我们引入树链剖分的关键——轻与重:

上图:

怎么树链剖分?

Step 1 dfs

这一步,我们要对树进行dfs,来确定一下几个内容:

code:

  1. LL w[N], son[N], fa[N], dep[N];
  2. il LL dfs(LL x, LL f, LL deep) {
  3. sz[x] = 1;
  4. dep[x] = deep;
  5. fa[x] = f;
  6. for (register int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {
  7. sz[x] += dfs(to[i], x, deep + 1);
  8. if (sz[to[i]] > sz[son[x]]) son[x] = to[i];
  9. }
  10. return sz[x];
  11. }

Step 2 rebuild

然后,我们对树进行重建,进行树链剖分,在这一步,我们要完成:

这里,我们要先处理重儿子,再处理轻儿子,这样就能保证每条重链一定在线性结构中连续,而重链又足够长,查询次数可以得到有效降低。

code:

  1. LL wt[N], id[N], sz[N], top[N], ncnt;
  2. il void Rebuild(LL x, LL f) { // f is current 'top'
  3. id[x] = ++ncnt;
  4. wt[ncnt] = w[x];
  5. top[x] = f;
  6. if (!son[x]) return;
  7. Rebuild(son[x], f);
  8. for (register int i = hd[x]; i; i = nxt[i]) {
  9. if (to[i] != fa[x] && to[i] != son[x]) {
  10. Rebuild(to[i], to[i]);
  11. }
  12. }
  13. }

Step 3 Change & Query

接下来就是修改和查询了,一个一个的讲:

1.链修改

给定一条链的左右端点x,y,将链上的每个节点增加某个值val
设更深的点为x,修改x到重链的顶端的权值。然后令x跳到顶端的父亲节点,重复这一过程,直至x, y在同一条重链中,这时,直接修改区间即可。
code:

  1. // Add is a funcition in SegmentTree,
  2. // it can fast add a value to a segment
  3. il void Achain(LL x, LL y, LL val) {
  4. val %= p;
  5. while (top[x] != top[y]) {
  6. if (dep[top[x]] < dep[top[y]]) swap(x, y);
  7. Add(1, 1, n, id[top[x]], id[x], val);
  8. x = fa[top[x]];
  9. }
  10. if (id[x] > id[y]) swap(x, y);
  11. Add(1, 1, n, id[x], id[y], val);
  12. }

2.链查询

给定一条链的左右端点x,y,返回链上的每个节点的权值和。
做法类似于修改。
code:

  1. // Sum is a funcition in SegmentTree,
  2. // it can fast return the sum of a segment
  3. il LL Qchain(LL x, LL y) {
  4. LL ans = 0;
  5. while (top[x] != top[y]) {
  6. if (dep[top[x]] < dep[top[y]]) swap(x, y);
  7. ans = (ans + Sum(1, 1, n, id[top[x]], id[x])) % p;
  8. x = fa[top[x]];
  9. }
  10. if (id[x] > id[y]) swap(x, y);
  11. ans = (ans + Sum(1, 1, n, id[x], id[y])) % p;
  12. printf("%lld\n", ans);
  13. }

3.子树修改与查询

使某个点及其子树的权值增加val
由于一棵子树在线性结构是连续的,直接对区间操作即可。
code:

  1. il void Ason(LL rt, LL val) {
  2. Add(1, 1, n, id[rt], id[rt] + sz[rt] - 1, val % p);
  3. }
  4. il LL Qson(LL rt) {
  5. printf("%d\n", Sum(1, 1, n, id[rt], id[rt] + sz[rt] - 1) % p);
  6. }

模板:LgP3384

codes:

  1. #include<cctype>
  2. #include<cstdio>
  3. #include<algorithm>
  4. using std::swap;
  5. #define il inline
  6. typedef long long LL;
  7. const int N = 100000 + 10;
  8. const int M = (N - 1) * 2 + 10;
  9. LL hd[N], to[M], nxt[M], cnt; // Tree
  10. LL w[N], son[N], fa[N], dep[N]; // Basic Info
  11. LL wt[N], id[N], sz[N], top[N], ncnt; // Tree Chain Split
  12. LL st[N << 2], add[N << 2]; // Segment Tree
  13. LL n, m, r, p; // Prob Info
  14. //---SegmentTree---//
  15. il LL ls(LL x) { return x << 1; }
  16. il LL rs(LL x) { return (x << 1) | 1; }
  17. il void PushDown(LL rt, LL len) {
  18. if (!add[rt]) return;
  19. (st[ls(rt)] += add[rt] * (len - (len >> 1)) % p) %= p;
  20. (st[rs(rt)] += add[rt] * (len >> 1) % p) %= p;
  21. (add[ls(rt)] += add[rt]) %= p;
  22. (add[rs(rt)] += add[rt]) %= p;
  23. add[rt] = 0;
  24. }
  25. il void Build(LL rt, LL l, LL r) {
  26. if (l == r) { st[rt] = wt[l]; return; }
  27. LL mid = (l + r) >> 1;
  28. Build(ls(rt), l, mid);
  29. Build(rs(rt), mid + 1, r);
  30. st[rt] = st[ls(rt)] + st[rs(rt)];
  31. }
  32. il void Add(LL rt, LL l, LL r, LL ql, LL qr, LL val) {
  33. if (ql <= l && qr >= r) {
  34. add[rt] = (add[rt] + val) % p;
  35. st[rt] = (st[rt] + (r - l + 1) * val) % p;
  36. return;
  37. }
  38. PushDown(rt, r - l + 1);
  39. LL mid = (l + r) >> 1;
  40. if (ql <= mid) Add(ls(rt), l, mid, ql, qr, val);
  41. if (qr > mid) Add(rs(rt), mid + 1, r, ql, qr, val);
  42. st[rt] = (st[ls(rt)] + st[rs(rt)]) % p;
  43. }
  44. il LL Sum(LL rt, LL l, LL r, LL ql, LL qr) {
  45. if (ql <= l && qr >= r) {
  46. return st[rt] % p;
  47. }
  48. PushDown(rt, r - l + 1);
  49. LL mid = (l + r) >> 1, ans = 0;
  50. if (ql <= mid) ans = (ans + Sum(ls(rt), l, mid, ql, qr)) % p;
  51. if (qr > mid) ans = (ans + Sum(rs(rt), mid + 1, r, ql, qr)) % p;
  52. return ans;
  53. }
  54. //---End---//
  55. //---树链剖分---//
  56. il LL dfs(LL x, LL f, LL deep) {
  57. sz[x] = 1;
  58. dep[x] = deep;
  59. fa[x] = f;
  60. for (register int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {
  61. sz[x] += dfs(to[i], x, deep + 1);
  62. if (sz[to[i]] > sz[son[x]]) son[x] = to[i];
  63. }
  64. return sz[x];
  65. }
  66. il void Rebuild(LL x, LL f) {
  67. id[x] = ++ncnt;
  68. wt[ncnt] = w[x];
  69. top[x] = f;
  70. if (!son[x]) return;
  71. Rebuild(son[x], f);
  72. for (register int i = hd[x]; i; i = nxt[i]) {
  73. if (to[i] != fa[x] && to[i] != son[x]) {
  74. Rebuild(to[i], to[i]);
  75. }
  76. }
  77. }
  78. il void Achain(LL x, LL y, LL val) {
  79. val %= p;
  80. while (top[x] != top[y]) {
  81. if (dep[top[x]] < dep[top[y]]) swap(x, y);
  82. Add(1, 1, n, id[top[x]], id[x], val);
  83. x = fa[top[x]];
  84. }
  85. if (id[x] > id[y]) swap(x, y);
  86. Add(1, 1, n, id[x], id[y], val);
  87. }
  88. il LL Qchain(LL x, LL y) {
  89. LL ans = 0;
  90. while (top[x] != top[y]) {
  91. if (dep[top[x]] < dep[top[y]]) swap(x, y);
  92. ans = (ans + Sum(1, 1, n, id[top[x]], id[x])) % p;
  93. x = fa[top[x]];
  94. }
  95. if (id[x] > id[y]) swap(x, y);
  96. ans = (ans + Sum(1, 1, n, id[x], id[y])) % p;
  97. printf("%lld\n", ans);
  98. }
  99. il void Ason(LL rt, LL val) {
  100. Add(1, 1, n, id[rt], id[rt] + sz[rt] - 1, val % p);
  101. }
  102. il LL Qson(LL rt) {
  103. printf("%d\n", Sum(1, 1, n, id[rt], id[rt] + sz[rt] - 1) % p);
  104. }
  105. //---End---//
  106. //---BasicOperate---//
  107. char c; LL tmp;
  108. il LL read() {
  109. while (!isdigit(c = getchar()));
  110. tmp = c ^ '0';
  111. while (isdigit(c = getchar())) tmp = (tmp << 3) + (tmp << 1) + (c ^ '0');
  112. return tmp;
  113. }
  114. il void adde(int x, int y) {
  115. cnt++;
  116. to[cnt] = y;
  117. nxt[cnt] = hd[x];
  118. hd[x] = cnt;
  119. }
  120. //---End---//
  121. int main() {
  122. n = read(); m = read(); r = read(); p = read();
  123. for (register int i = 1; i <= n; ++i) w[i] = read();
  124. for (register int i = 1, x, y; i < n; ++i) {
  125. x = read(); y = read();
  126. adde(x, y); adde(y, x);
  127. }
  128. dfs(r, 0, 1);
  129. Rebuild(r, r);
  130. Build(1, 1, n);
  131. for (register int i = 1, x, y, z, op; i <= m; ++i) {
  132. op = read();
  133. switch(op) {
  134. case 1 :
  135. x = read(); y = read(); z = read();
  136. Achain(x, y, z);
  137. break;
  138. case 2 :
  139. x = read(); y = read();
  140. Qchain(x, y);
  141. break;
  142. case 3 :
  143. x = read(); z = read();
  144. Ason(x, z);
  145. break;
  146. case 4 :
  147. x = read();
  148. Qson(x);
  149. break;
  150. }
  151. }
  152. return 0;
  153. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注