树链剖分模板

LuoguP3384

对于这个树剖吧。。。一开始不打算学的。。。但是貌似挺有用的,于是乎下狠心学了一哈。本蒟蒻初学树剖可能理解不太透彻,如有不对之处还请各位巨佬指正


 先来一个树剖的了解  IvanovCraft的博客

首先我们需要了解一下树链剖分可以用来干什么:

1.可以求树上差分,LCA(时间复杂度会小一些)

2.最重要的是可以修改树上的边权或者点权(比如本题)

剩下的目前本蒟蒻还too vegetable,所以对其理解不够深,许多用法可能以后会更新

貌似大佬们看看上面博客就好了

Code:

  1 #include <bits/stdc++.h>
  2 #define ls x << 1
  3 #define rs x << 1 | 1
  4 using namespace std;
  5 const int N = 200001;
  6 int n, m, r, p;
  7 int tot, head[N];//邻接链表存边 
  8 struct edge{
  9     int net, v;
 10 }e[N];
 11 int a[N * 4], v[N * 4];
 12 int cnt, w[N], wt[N], son[N], d[N], siz[N], fa[N], id[N], top[N];
 13 //w[]存读入的点,wt[]存dfs遍历后的点,son[]存每个节点的重儿子,d[]存每个点的深度,size[]存以当前节点为根节点的子树的大小
 14 //fa[]存每个节点的父亲节点,id[]存每个点的dfs序,top[]存当前节点所在重链的根节点
 15 void add(int u, int v) {
 16     e[++tot].net = head[u];
 17     e[tot].v = v;
 18     head[u] = tot;
 19 }
 20 void push(int x, int l) {//更新懒标 
 21     v[ls] += v[x];
 22     v[rs] += v[x];
 23     a[ls] += v[x] * (l - l / 2);//这里上下互换也可以,只是为了将这条链分成两部分。 
 24                                 //又考虑到长度不可能全是偶数,所以一个上取整,另一个下取整。 
 25     a[rs] += v[x] * (l / 2);
 26     a[ls] %= p;
 27     a[rs] %= p;
 28     v[x] = 0;
 29 }
 30 void build(int x, int l, int r) {
 31     if (l == r) {
 32         a[x] = wt[l];//wt[]数组存的是dfs遍历后的点 
 33         a[x] %= p;
 34         return;
 35     }
 36     int mid = (l + r) / 2;
 37     build(ls, l, mid);
 38     build(rs, mid + 1, r);
 39     a[x] = (a[ls] + a[rs]) % p;
 40 }
 41 int query(int x, int l, int r, int L, int R) {//线段树维护和 
 42     int res = 0;
 43     if (L <= l && r <= R) {
 44         res += a[x];
 45         res %= p;
 46         return res;
 47     }
 48     if (v[x]) {
 49         push(x, r - l + 1);
 50     }
 51     int mid = (l + r) / 2;
 52     if (L <= mid) {
 53         res += query(ls, l, mid, L, R);
 54     }
 55     if (R > mid) {
 56         res += query(rs, mid + 1, r, L, R);
 57     }
 58     return res % p;
 59 }
 60 void change(int x, int l, int r, int L, int R, int k) {//十分正常的修改 
 61     if (L <= l && r <= R) {
 62         v[x] += k;
 63         a[x] += k * (r - l + 1);
 64         a[x] %= p;
 65         return;
 66     }
 67     if (v[x]) {
 68         push(x, r - l + 1);
 69     }
 70     int mid = (l + r) / 2;
 71     if (L <= mid) {
 72         change(ls, l, mid, L, R, k);
 73     }
 74     if(R > mid) {
 75         change(rs, mid + 1, r, L, R, k);
 76     }
 77     a[x] = (a[ls] + a[rs]) % p;
 78 }
 79 void Qchange(int x, int y, int k) {
 80     k %= p;
 81     while (top[x] != top[y]) {//当这两个点不在同一条重链上 
 82         if (d[top[x]] < d[top[y]]) {//调整深度,让x变成更深的那一个
 83             swap(x, y);
 84         }
 85         change(1, 1, n, id[top[x]], id[x], k);//修改 
 86         x = fa[top[x]];//每次结束后都要跳到父亲那里 
 87     }
 88     if (d[x] > d[y]) {//当两节点在一条重链上但不是同一节点时
 89                       //调整深度 
 90         swap(x, y);
 91     }
 92     change(1, 1, n, id[x], id[y], k);//在最后的距离上进行修改 
 93 }
 94 int Qquery(int x, int y) {
 95     int ans = 0;
 96     while (top[x] != top[y]) {//当这两个点不在同一条重链上 
 97         if (d[top[x]] < d[top[y]]) {//调整深度,让x变成更深的那一个
 98             swap(x, y);
 99         }
100         ans += query(1, 1, n, id[top[x]], id[x]);//累加答案 
101         ans %= p;
102         x = fa[top[x]];//每次结束后都要跳到父亲那里 
103     }
104     if (d[x] > d[y]) {
105         swap(x, y);
106     }
107     ans += query(1, 1, n, id[x], id[y]);
108     return ans % p;
109 }
110 void Schange(int x, int k) {
111     change(1, 1, n, id[x], id[x] + siz[x] - 1, k);
112     //因为要修改以x为根节点的子树,所以边界就是 id[x] 到 id[x] + siz[x] - 1
113 }
114 int Squery(int x) {
115     return query(1, 1, n, id[x], id[x] + siz[x] - 1);//同上 
116 }
117 void dfs1(int x, int f, int deep) {//核心函数1
118     d[x] = deep;//深度 
119     fa[x] = f;//父亲节点
120     siz[x] = 1;//当前节点大小为1 
121     int maxson = -1;
122     for (int i = head[x]; i; i = e[i].net) {//枚举所有出边 
123         int to = e[i].v;
124         if (to == f) {//肯定不能回到自己父亲吧~ 
125             continue;
126         }
127         dfs1(to, x, deep + 1);//继续dfs 
128         siz[x] += siz[to];//大小累加 
129         if (siz[to] > maxson) {//跟新重儿子 
130             son[x] = to;
131             maxson = siz[to];
132         }
133     }
134 }
135 void dfs2(int x, int topf) {//核心函数2 
136     id[x] = ++cnt;//按照重链dfs来储存新的dfs序 
137     wt[cnt] = w[x];//储存下来 
138     top[x] = topf;//因为在同一重链上,直接赋值 
139     if (!son[x]) {//如果当前节点没有重儿子就停止 
140         return;
141     }
142     dfs2(son[x], topf);//优先重儿子进行深搜 
143     for (int i = head[x]; i; i = e[i].net) {
144         int to = e[i].v;
145         if (to == fa[x] || to == son[x]) {//如果是自己的父亲或者是重儿子就跳过 
146             continue;
147         }
148         dfs2(to, to);//因为轻儿子不在重链上,所以它的top就是自己 
149     }
150 }
151 int main() {
152     scanf("%d%d%d%d", &n, &m, &r, &p);
153     for (int i = 1; i <= n; i++) {
154         scanf("%d", &w[i]);
155     }
156     for (int i = 1; i < n; i++) {
157         int u, v;
158         scanf("%d%d", &u, &v);
159         add(u, v);
160         add(v, u);
161     }
162     dfs1(r, 0, 1);
163     dfs2(r, r);
164     build(1, 1, n);
165     for (int i = 1; i <= m; i++) {
166         int o, x, y, z;
167         scanf("%d", &o);
168         if (o == 1) {
169             scanf("%d%d%d", &x, &y, &z);
170             Qchange(x, y, z);
171         } else if (o == 2) {
172             scanf("%d%d", &x, &y);
173             printf("%d
", Qquery(x, y));
174         } else if (o == 3) {
175             scanf("%d%d", &x, &z);
176             Schange(x, z);
177         } else {
178             scanf("%d", &x);
179             printf("%d
", Squery(x));
180         }
181     }
182     return 0;
183 }
View Code
原文地址:https://www.cnblogs.com/Sundial/p/11830434.html