树链剖分模板 洛谷P3384
作者:互联网
#include <cstdio>
const int maxn = 200000+10;
struct edge{
int v, next;
}e[maxn << 1];
//N、M、R、mod,分别表示树的结点个数、操作个数、根节点序号和取模数
int n, m ,r , mod, len = 1, cnt, code , x, y, z; //cnt是重新进行编号的 编号
int w[maxn]; //保存每个节点的权值
int wt[maxn]; //保存新编号后节点的权值
int top[maxn];//保存每条重链的开头节点
int id[maxn]; //节点重新编号后的编号
int fa[maxn]; //保存每个节点的父亲节点
int deep[maxn]; //保存每个节点的深度 根节点的深度是1,依次往下递增
int son[maxn]; //记录每个节点的重儿砸是谁
int sz[maxn]; //记录每个节点及其子树的节点数量
int sum[maxn << 2], lazy[maxn << 2]; //线段树维护的区间和 lazy标记
int head[maxn]; //默认都是0 表示没有边
//添加双向边
void add(int u, int v) {
e[len].v = v;
e[len].next = head[u];
head[u] = len++;
}
//u是当前节点 fa是父节点(根节点的父节点是0) deep代表节点深度
void dfs1(int u, int f, int dep) {
deep[u] = dep; // 记录每个节点深度
fa[u] = f;
sz[u] = 1;
int sonW = -1; //来记录重儿子的重量
for (int j = head[u]; j; j = e[j].next) {
int v = e[j].v;
if (v == f) continue;
dfs1(v, u, dep + 1);
sz[u] += sz[v]; //加上儿子的重量
//更新重儿子
if (sonW < sz[v]) {
son[u] = v;
sonW = sz[v];
}
}
}
//给节点重新编号 并计算出重链的top
void dfs2(int u, int tp) {
id[u] = ++cnt; //对节点进行重新编号
wt[cnt] = w[u];//更新新编号后的权值
top[u] = tp; //记录当前节点所在重链的top
if (son[u]) {
//存在重儿子 先走重儿子这条路
dfs2(son[u], tp);
}
//创造其他重链, 以其它轻儿子为头
for (int j = head[u]; j; j = e[j].next) {
int v = e[j].v;
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v); //以自己为top的头
}
}
void puttag(int id, int l, int r, int v) {
sum[id] += (r - l + 1) * v;
lazy[id] += v;
}
void pushdown(int id, int l, int r) {
if (lazy[id]) {
int mid = (l + r) >> 1;
int ll = id << 1;
int rr = id << 1 | 1;
lazy[ll] += lazy[id];
lazy[rr] += lazy[id];
sum[ll] = (sum[ll] + (mid - l + 1) * lazy[id]) % mod;
sum[rr] = (sum[rr] + (r - mid) * lazy[id]) % mod;
lazy[id] = 0;
}
}
void build(int id, int l, int r) {
if (l == r) {
sum[id] = wt[l] % mod ;
return;
}
int mid = (l + r) >> 1;
build(id << 1, l , mid);
build(id << 1 | 1, mid + 1, r);
sum[id] = (sum[id << 1] + sum[id << 1 | 1] )% mod;
}
void update(int id, int l, int r, int x, int y, int v) {
if (x <= l && r <= y) {
puttag(id, l, r, v);
return;
}
//标记下放
pushdown(id, l ,r);
int mid = (l + r) >> 1;
if (x <= mid) {
update(id << 1, l, mid, x, y, v);
}
if (y > mid) {
update(id << 1 | 1, mid + 1, r, x, y, v);
}
sum[id] = (sum[id << 1] + sum[id << 1 | 1] )% mod;
}
int query(int id, int l, int r, int x, int y) {
if (x <= l && r <= y) {
return sum[id];
}
int mid = (l + r) >> 1;
int ans = 0;
//下方标记
pushdown(id, l ,r);
if (x <= mid) {
ans = (ans + query(id << 1, l, mid, x, y)) % mod;
}
if (y > mid) {
ans = (ans + query(id << 1 | 1, mid + 1, r, x, y)) % mod;
}
return ans;
}
void swap(int &x, int &y) {
int tem = x;
x = y;
y = tem;
}
//1 x y z x节点到y节点的节点都加上z
void xToyAdd(int x, int y, int z) {
while (top[x] != top[y]) {
//把x节点变成深度更深的那个点
if (deep[top[x]] < deep[top[y]]) swap(x, y);
//将当前x的重链上的所有节点都加上z
update(1, 1, cnt, id[top[x]], id[x], z);
//x变成该重链头的父亲节点
x = fa[top[x]];
}
//x 与 y节点在同一重链上 把x变成深度小的点
if (deep[x] > deep[y]) swap(x, y);
update(1, 1, cnt, id[x], id[y], z);
}
// 2 x y 求x节点到y节点的和
int xToySum(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (deep[top[x]] < deep[top[y]]) swap(x, y);
ans = (ans + query(1, 1, cnt, id[top[x]], id[x])) % mod;
x = fa[top[x]];
}
if (deep[x] > deep[y]) swap(x, y);
ans = (ans + query(1, 1, cnt, id[x], id[y])) % mod;
return ans % mod;
}
//3 x z 表示将以x为根节点的子树内所有节点值都加上z
void xAdd(int x, int z) {
update(1, 1, cnt, id[x], id[x] + sz[x] - 1, z);
}
// 4 x 表示求以x为根节点的子树内所有节点值之和
int xSum(int x) {
return query(1, 1, cnt, id[x], id[x] + sz[x] - 1) ;
}
int main () {
scanf("%d%d%d%d", &n, &m, &r, &mod);
for (int i = 1; i <= n; i++) {
scanf("%d", w + i);
}
int u, v;
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
//2次dfs
dfs1(r, 0, 1);
dfs2(r, r);
//进行建树操作
build(1, 1, cnt);
//m行操作
for (int i = 1; i <= m; i++) {
scanf("%d", &code);
if (code == 1) {
scanf("%d%d%d", &x, &y, &z);
xToyAdd(x, y, z % mod);
} else if (code == 2) {
scanf("%d%d", &x, &y);
printf("%d\n", xToySum(x, y));
} else if(code == 3) {
scanf("%d%d", &x, &z);
xAdd(x, z % mod);
} else {
scanf("%d", &x);
printf("%d\n", xSum(x));
}
}
return 0;
}
标签:cnt,洛谷,剖分,int,top,deep,P3384,ans,id 来源: https://blog.csdn.net/qq_41280600/article/details/100780687