一份30分(1,3,4)的代码,其余点WA(2,5,6,7,8,9,10)掉。 代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int n, m, r, mod, hd[N], tot, w[N], rank[N], tim;
long long val[N << 2], tag[N << 2];
// n : 节点个数
// m : 操作个数
// r : 根节点序号
// mod : 取模数
// hd[x] : <链式前向星> 节点x最后一条边的编号
// tot : <链式前向星> 记录总边数
// w[x] : 节点x的初始值
// rank[x] : 时间戳为x的节点编号
// tim : 时间标记
struct Node{ // 节点参数
int fa, dep, siz, hson, top, dfn, low;
// fa : 父节点编号
// dep : 深度
// siz : 子树大小
// hson : 重儿子编号
// top : 所在重链链首
// dfn : 时间戳
// low : 子树遍历结束的时间戳
}node[N];
struct Edge{ // 边参数
int ed, nxt;
// ed : 终点
// nxt : 上一条边
}edge[N << 1];
void add(int x, int y) { // 链式前向星存图
edge[++tot].ed = y;
edge[tot].nxt = hd[x];
hd[x] = tot;
}
// 线段树部分
void build(int p, int l, int r) { // 建树
if (l == r) return (void) (val[p] = w[rank[l]]); // 叶子结点的权值是第 l (或 r ) 个时间戳对应的节点
int mid = l + r >> 1;
build(p << 1, l, mid); // 左儿子
build(p << 1 | 1, mid + 1, r); // 右儿子
val[p] = (val[p << 1] + val[p << 1 | 1]) % mod; // pushup
return;
}
void pushdown(int p, int l, int r) { // 下放lazytag
tag[p << 1] = (tag[p] + tag[p << 1]) % mod, tag[p << 1 | 1] = (tag[p] + tag[p << 1 | 1]) % mod; // tag直接传到儿子
int mid = l + r >> 1;
val[p << 1] = (tag[p] * (mid - l + 1) + val[p << 1]) % mod,
val[p << 1 | 1] = (tag[p] * (r - mid) + val[p << 1 | 1]) % mod; // val值加上区间个数乘tag
tag[p] = 0; // 父节点tag清空
}
void modify(int p, int l, int r, int L, int R, int k) { // 修改时间戳为 L 到 R 区间的值,当前编号p, 区间l到r
if (l >= L && r <= R) { // 如果当前区间为待修改区间的子区间
tag[p] = (tag[p] + k) % mod; // lazytag 加上 k
val[p] = (val[p] + (r - l + 1) * k) % mod; // val 加上区间长度乘k
return;
}
if (tag[p]) pushdown(p, l, r); // 下放标记
int mid = l + r >> 1;
if (mid >= L) modify(p << 1, l, mid, L, R, k); // 如果左儿子含有待修改区间,修改左儿子
if (mid < R) modify(p << 1 | 1, mid + 1, r, L, R, k); // 如果右儿子含有待修改区间,修改右儿子
val[p] =(val[p << 1] + val[p << 1 | 1]) % mod; // pushup
return;
}
long long query(int p, int l, int r, int L, int R) { // 询问时间戳为 L 到 R的节点加和, 当前节点编号p, 区间l到r
if (l >= L && r <= R) return val[p]; // 该区间为询问区间的子区间
if (tag[p]) pushdown(p, l, r); // 下放标记
int mid = l + r >> 1;
long long ans = 0;
if (mid >= L) ans = query(p << 1, l, mid ,L, R); // 如果左儿子含有询问区间,ans加上左儿子的贡献
if (mid < R) ans = (ans + query(p << 1 | 1, mid + 1, r, L, R)) % mod; // 如果右儿子含有询问区间, ans加上右儿子的贡献
return ans;
}
// 以下为树剖函数
void TREE_build(int u, int des) { // 以深度为des的节点u为根节点,建树,统计子树大小并确定重儿子
node[u].siz = 1; // 初始大小为1
for (int i = hd[u]; i; i = edge[i].nxt) { // 遍历边
int v = edge[i].ed; // 取该边终点
if (v == node[u].fa) continue; // 如果扫到父节点,就跳过
node[v].fa = u; // 将v的父节点设为u
node[v].dep = des + 1; // 子节点深度为父节点深度加1
TREE_build(v, des + 1); // 建儿子
node[u].siz += node[v].siz; // 父节点大小加上子节点大小
if (!node[u].hson || node[v].siz > node[node[u].hson].siz) node[u].hson = v;
// 如果父节点还没有重儿子 或 该儿子的子树大小大于原有的重儿子, 更新重儿子
}
}
void TREE_decomposition(int u, int top) { // 当前节点为u,所在重链的链顶为top,划分重链并标记时间戳
node[u].top = top; // 记录点u链顶为top
node[u].dfn = ++tim; // 记录时间戳
rank[tim] = u; // 记录时间戳为tim的节点为u
if (node[u].hson) TREE_decomposition(node[u].hson, top); // 如果有重儿子,传递链顶
for (int i = hd[u]; i; i = edge[i].nxt) { // 遍历轻儿子
int v = edge[i].ed; // 取该边终点
if (v == node[u].hson || v == node[u].fa) continue; // 如果终点为父节点或重儿子,就跳过
TREE_decomposition(v, v); // 对轻儿子,以其自身为链顶继续剖分新的重链
}
node[u].low = tim; // 以u为根的子树最后一个节点的时间戳
}
void TREE_path_modify(int u, int v, int k) { // 对从u到v的路径进行修改
while (node[u].top != node[v].top) { // 如果u,v不在同一条重链
if (node[node[u].top].dep < node[node[v].top].dep) swap(u, v); // 确保u在跳跃到链顶时,深度大于v
modify(1 , 1, n, node[u].dfn, node[node[u].top].dfn, k); // 修改从u到重链链顶的值
u = node[node[u].top].fa; // 将u跳至其所在重链链顶的父节点
}
if (node[u].dfn > node[v].dfn) swap(u, v); // 确保时间戳u在v左侧
modify(1, 1, n, node[u].dfn, node[v].dfn, k); // 修改u到v的权值
return;
}
long long TREE_path_sum(int u, int v) { // 询问u到v路径上的权值和
long long ans = 0;
while (node[u].top != node[v].top) { // 如果u,v不在同一条重链
if (node[node[u].top].dep < node[node[v].top].dep) swap(u, v);// 确保u在跳跃到链顶时,深度大于v
ans = (ans + query(1, 1, n, node[u].dfn, node[node[u].top].dfn)) % mod; // ans加上从u到重链链顶的权值和
u = node[node[u].top].fa; // 将u跳至其所在重链链顶的父节点
}
if (node[u].dfn > node[v].dfn) swap(u, v); // 确保时间戳u在v左侧
ans = (ans + query(1, 1, n, node[u].dfn, node[v].dfn)) % mod; // ans 加上u到v的权值和
return ans;
}
void TREE_root_modify(int u, int k) { // 修改以u为根子树的权值
modify(1, 1, n, node[u].dfn, node[u].low, k);
return;
}
long long TREE_root_sum(int u) { // 询问以u为根子树的权值和
return query(1, 1, n, node[u].dfn, node[u].low);
}
int main() {
scanf("%d%d%d%d", &n, &m, &r, &mod);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]); // 读入各点初始权值
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x); // 建立无向边
}
TREE_build(r, 0); // 建立以r为根的树, r的深度为0
TREE_decomposition(r, r); // 以r为链顶,从r开始剖分重链
build(1, 1, n); // 建立线段树
for (int i = 1; i <= m; i++) {
int op;
scanf("%d", &op);
if (op == 1) { // 修改x到y路径上节点的权值
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
TREE_path_modify(x, y, z);
}
if (op == 2) { // 询问x到y路径上节点的权值和
int x, y;
scanf("%d%d", &x, &y);
printf("%lld\n", TREE_path_sum(x, y));
}
if (op == 3) { // 修改以x为根的子树的权值
int x, y;
scanf("%d%d", &x, &y);
TREE_root_modify(x, y);
}
if (op == 4) { // 查询以x为根的子树的权值和
int x;
scanf("%d", &x);
printf("%lld\n", TREE_root_sum(x));
}
}
return 0;
}
感谢。