大佬博客:http://www.cnblogs.com/chinhhh/p/7965433.html#dfs1
模板题: https://www.luogu.org/problemnew/show/P3384
重儿子(非叶子结点):它某个点的作为儿子,且是儿子中子树结点数最多的儿子。
轻儿子(非叶子结点):非重儿子
重边:一个父亲连接他的重儿子的边称为重边
轻边:非重边
重链:
- 相邻重边连起来
- 连接一个重儿子
- 每个叶子结点,如果是轻儿子,则有一条以自己为起点的长度为1的链
把csdn当做云盘
模板
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5+5;
// 变量
int n,m,mod;
vector<int> G[maxn];
int w[maxn]; // 点权值
// 线段树
int val[maxn<<2], lazy[maxn<<2];
// 树链剖分
int son[maxn], id[maxn], fa[maxn],dep[maxn],siz[maxn],top[maxn],wt[maxn];
int cnt;
//------------------------------
// 线段树部分
void pushup(int rt) {
val[rt] = (val[rt<<1]+val[rt<<1|1])%mod;
}
void pushdown(int rt, int l,int r) {
if(lazy[rt]) {
int mid = (l+r) >> 1;
val[rt<<1] += lazy[rt]*(mid-l+1);
val[rt<<1|1] += lazy[rt]*(r-mid);
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
lazy[rt] = 0;
val[rt<<1] %= mod;
val[rt<<1|1] %= mod;
lazy[rt<<1] %= mod;
lazy[rt<<1|1] %= mod;
}
}
void build(int l, int r,int rt) {
if(l == r) {
val[rt] = wt[l];
return;
}
int mid = (l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int L, int R, int k,int l, int r,int rt) {
if(L > r || R < l) return;
if(L <= l && r <= R) {
val[rt] += k*(r-l+1);
val[rt] %= mod;
lazy[rt] += k;
lazy[rt] %= mod;
return;
}
int mid = (l+r)>>1;
pushdown(rt,l,r);
update(L,R,k,lson);
update(L,R,k,rson);
pushup(rt);
}
int query(int L, int R, int l,int r, int rt) {
if(L > r || R < l) return 0;
if(L <= l && r <= R) {
return val[rt];
}
int mid = (l+r)>>1;
int res = 0;
pushdown(rt,l,r);
res += query(L,R,lson);
res %= mod;
res += query(L,R,rson);
res %= mod;
pushup(rt);
return res;
}
//------------------------------
//树链剖分,操作
// 求和
int qRange(int x,int y) {
int ans = 0;
while(top[x] != top[y]) { // 让其跑到一条链上
if(dep[top[x]] < dep[top[y]]) swap(x,y);
ans += query(id[top[x]],id[x],1,n,1);
ans %= mod;
x = fa[top[x]];
}
// 在同一条链上了
if(dep[x] > dep[y]) swap(x,y);
ans += query(id[x],id[y],1,n,1);
ans %= mod;
return ans;
}
// 更新
void updRange(int x,int y, int k) {
k %= mod;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
update(id[top[x]],id[x],k,1,n,1);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
update(id[x],id[y],k,1,n,1);
}
int qSon(int x) {
return query(id[x],id[x]+siz[x]-1,1,n,1);
}
void updSon(int x, int k) {
update(id[x],id[x]+siz[x]-1,k,1,n,1);
}
//------------------------------
// 树链剖分, 处理链
// u 当前结点,f父亲,deep深度
void dfs1(int u,int f,int deep) {
dep[u] = deep;
fa[u] = f;
siz[u] = 1;
int maxson=-1; // 重儿子子树大小
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == f) continue;
dfs1(v,u,deep+1);
siz[u] += siz[v];
if(siz[v] > maxson) {
son[u] = v;
maxson = siz[v];
}
}
}
// u 当前结点,topf当前链的最顶端的节点
void dfs2(int u, int topf) {
id[u] = ++cnt; // u点编号为cnt
wt[cnt] = w[u]; // 记录新编号的权值
top[u] = topf; // 该点所在链的链头
if(!son[u]) return;
dfs2(son[u], topf); // 先处理重儿子
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v,v); // 每个轻儿子都有从自己开始的链
}
}
//------------------------------
int main() {
int q,rt;
scanf("%d%d%d%d", &n, &q, &rt, &mod);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
int u,v;
for(int i = 0; i < n-1; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(rt,-1,1);
dfs2(rt,rt);
build(1,n,1);
int opt,x,y,z;
for(int i = 0; i < q; ++i) {
scanf("%d", &opt);
if(opt == 1) {
scanf("%d%d%d", &x, &y, &z);
updRange(x,y,z);
} else if(opt == 2) {
scanf("%d%d", &x, &y);
printf("%d\n", qRange(x,y));
} else if(opt == 3) {
scanf("%d%d", &x, &z);
updSon(x,z);
} else if(opt == 4) {
scanf("%d", &x);
printf("%d\n", qSon(x));
}
}
return 0;
}