树链剖分详情:跳转大佬博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html
题目链接:https://www.luogu.org/problemnew/show/P3384
解题心得:
个人看来其实树链剖分就是把一棵标号没有实际意义的树重新标号,标号的规则按照重链优先,在有序之后用线段树之类的数据结构来维护。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
struct Node {
int sum, lazy;
}node[maxn<<2];
int num[maxn], n, m, rt, mod;
int Size[maxn], deep[maxn], max_son[maxn], father[maxn], top[maxn], rk[maxn], id[maxn], cnt;
/*
Size[i] 代表以i为根节点的子树的大小
deep[i] 代表节点i的深度
max_son[i] 代表以i的子节点为根节点的树最大的子节点节点标号
father[i] 表示i的父亲是谁
top[i] 代表i所在的重链的根节点
id[i] 代表标号为i的节点剖分之后的数
rk[i] 剖分之后数为i的节点的标号是多少
*/
vector <int> ve[maxn];
void init() {
scanf("%d%d%d%d",&n, &m, &rt, &mod);
for(int i=1;i<=n;i++) scanf("%d", &num[i]);
for(int i=1;i<n;i++) {
int a, b; scanf("%d%d", &a, &b);
ve[a].push_back(b);
ve[b].push_back(a);
}
}
int dfs(int pre, int now, int Deep) {
Size[now] = 1;
deep[now] = Deep;
father[now] = pre;
int Max = 0;
for(int i=0;i<ve[now].size();i++) {
int v = ve[now][i];
if(v != pre) {
Size[now] += dfs(now, v, Deep+1);
if(Size[v] > Max) {
Max = Size[v];
max_son[now] = v;
}
}
}
return Size[now];
}
void dfs2(int pre, int now, int Top) {
top[now] = Top;
id[now] = ++cnt;
rk[cnt] = now;
if(!max_son[now]) return ;
dfs2(now, max_son[now], Top);
for(int i=0;i<ve[now].size();i++) {
int v = ve[now][i];
if(v != pre && v != max_son[now]) {
dfs2(now, v, v);
}
}
}
void pushup(int root) {
int chl = root<<1;
int chr = root<<1|1;
node[root].sum = (node[chl].sum + node[chr].sum)%mod;
}
void push_down(int root, int l, int r) {
if(node[root].lazy == 0) return ;
int chl = root <<1 ;
int chr = root<<1|1;
int mid = l + r >> 1;
node[chl].lazy = (node[chl].lazy + node[root].lazy) % mod;
node[chl].sum = (node[chl].sum + (mid-l + 1) * node[root].lazy) % mod;
node[chr].lazy = (node[chr].lazy + node[root].lazy) % mod;
node[chr].sum = (node[chr].sum + (r - mid) * node[root].lazy) % mod;
node[root].lazy = 0;
}
void build_tree(int root, int l, int r) {
if(l == r) {
int Id = rk[l];
node[root].sum = num[Id];
return ;
}
int mid = l + r >> 1;
build_tree(root<<1, l, mid);
build_tree(root<<1|1, mid+1, r);
pushup(root);
}
void build_tree() {
dfs(-1, rt, 1);
dfs2(-1, rt, rt);
build_tree(1, 1, n);
}
void add(int root, int ql, int qr, int l, int r, int c) {
if(ql == l && qr == r) {
node[root].lazy += c;
node[root].sum = (node[root].sum + (r - l + 1) * c) % mod;
return ;
}
push_down(root, l, r);
int mid = l + r >> 1;
if(mid >= qr)
add(root<<1, ql, qr, l, mid, c);
else if(mid < ql) {
add(root<<1|1, ql, qr, mid+1, r, c);
} else {
add(root<<1, ql, mid, l, mid, c);
add(root<<1|1, mid+1, qr, mid+1, r, c);
}
pushup(root);
}
void update(int l, int r, int c) {
while(top[l] != top[r]) {
if(deep[top[l]] < deep[top[r]]) swap(l, r);
add(1, id[top[l]], id[l], 1, n, c);
l = top[l]; l = father[l];
}
if(id[l] > id[r]) swap(l, r);
add(1, id[l], id[r], 1, n, c);
}
int query(int root, int ql, int qr, int l, int r) {
if(ql == l && qr == r) {
return node[root].sum;
}
push_down(root, l, r);
int mid = l + r >> 1;
if(mid >= qr) {
return query(root<<1, ql, qr, l, mid) % mod;
} else if(mid < ql) {
return query(root<<1|1, ql, qr, mid+1, r) % mod;
} else {
return (query(root<<1, ql, mid, l, mid) + query(root<<1|1, mid+1, qr, mid+1, r)) % mod;
}
}
int query(int l, int r) {
int Sum = 0;
while(top[l] != top[r]) {
if(deep[top[l]] < deep[top[r]]) swap(l, r);
Sum += query(1, id[top[l]], id[l], 1, n);
Sum %= mod;
l = top[l]; l = father[l];
}
if(id[l] > id[r]) swap(l, r);
Sum += query(1, id[l], id[r], 1, n);
return Sum % mod;
}
int main() {
// freopen("1.in", "r", stdin);
init();
build_tree();
while(m--) {
int op; scanf("%d", &op);
if(op == 1) {
int a, b, c;
scanf("%d%d%d",&a, &b, &c);
update(a, b, c);
} else if(op == 2) {
int a, b; scanf("%d%d", &a, &b);
printf("%d\n", query(a, b));
} else if(op == 3) {
int pos, c; scanf("%d%d",&pos, &c);
add(1, id[pos], id[pos]+Size[pos]-1, 1, n, c);
} else {
int pos; scanf("%d", &pos);
printf("%d\n", query(1, id[pos], id[pos]+Size[pos]-1, 1, n));
}
}
return 0;
}