From c0f663fbe5ebbc4d612fa4e60f5f51d9a907d3bf Mon Sep 17 00:00:00 2001 From: hanyixuanten Date: Thu, 29 Oct 2026 02:05:11 +0000 Subject: [PATCH] new file: P3384.cpp --- P3384.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ P3384.md | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 P3384.cpp create mode 100644 P3384.md diff --git a/P3384.cpp b/P3384.cpp new file mode 100644 index 0000000..c3f1d92 --- /dev/null +++ b/P3384.cpp @@ -0,0 +1,125 @@ +#include +using namespace std; +int n, m, r, p; +int a[100005]; +vector fri[100005]; +int dep[100005]; +int cnt[100005]; +int fa[100005]; +int son[100005]; +int top[100005]; +int dfn[100005]; +int nowdfn = 0; +struct seg{ + int num[400005], lt[400005]; + void pu(int now) { + num[now] = (num[now * 2] + num[now * 2 + 1]) % p; + } + void pd(int now, int nl, int nr) { + int l = lt[now], nmid = (nl + nr) >> 1; + num[now * 2] = (1ll * num[now * 2] + l * (nmid - nl + 1)) % p; + num[now * 2 + 1] = (1ll * num[now * 2 + 1] + l * (nr - nmid)) % p; + lt[now * 2] = (lt[now * 2] + l) % p; + lt[now * 2 + 1] = (lt[now * 2 + 1] + l) % p; + lt[now] = 0; + } + void add(int now, int nl, int nr, int l, int r, int nn) { + if(nl > r || nr < l) return; + if(l <= nl && nr <= r) { + num[now] = (1ll * (nn) * (nr - nl + 1) + num[now]) % p; + lt[now] = (lt[now] + nn) % p; + return; + } + int nmid = (nl + nr) >> 1; + pd(now, nl, nr); + add(now * 2, nl, nmid, l, r, nn); + add(now * 2 + 1, nmid + 1, nr, l, r, nn); + pu(now); + } + int query(int now, int nl, int nr, int l, int r) { + if(nl > r || nr < l) return 0; + if(l <= nl && nr <= r) { + return num[now] % p; + } + int nmid = (nl + nr) >> 1; + pd(now, nl, nr); + return (query(now * 2, nl, nmid, l, r) + query(now * 2 + 1, nmid + 1, nr, l, r)) % p; + } +}seg; +void dfs1(int now, int from) { + cnt[now] = 1; + for(int i : fri[now]) { + if(i == from) continue; + fa[i] = now; + dep[i] = dep[now] + 1; + dfs1(i, now); + cnt[now] += cnt[i]; + if(cnt[i] > cnt[son[now]]) son[now] = i; + } +} +void dfs2(int now, int from) { + dfn[now] = ++nowdfn; + top[now] = from; + // bit.add(nowdfn, nowdfn, a[now]); + seg.add(1, 1, n, nowdfn, nowdfn, a[now]); + if(son[now]) + dfs2(son[now], top[now]); + for(int i : fri[now]) { + if(i == fa[now] || i == son[now]) continue; + dfs2(i, i); + } +} +void pathadd(int x, int y, int nn) { + while(top[x] != top[y]) { + if(dep[top[x]] < dep[top[y]]) swap(x, y); + seg.add(1, 1, n, dfn[top[x]], dfn[x], nn); + x = fa[top[x]]; + } + if(dep[x] < dep[y]) swap(x, y); + seg.add(1, 1, n, dfn[y], dfn[x], nn); +} +int query(int x, int y) { + int ans = 0; + while(top[x] != top[y]) { + if(dep[top[x]] < dep[top[y]]) swap(x, y); + ans = (ans + seg.query(1, 1, n, dfn[top[x]], dfn[x])) % p; + x = fa[top[x]]; + } + if(dep[x] < dep[y]) swap(x, y); + return (ans + seg.query(1, 1, n, dfn[y], dfn[x])) % p; +} +void adds(int x, int nn) { + seg.add(1, 1, n, dfn[x], dfn[x] + cnt[x] - 1, nn); +} +int querys(int x) { + return seg.query(1, 1, n, dfn[x], dfn[x] + cnt[x] - 1) % p; +} +int main() { + scanf("%d%d%d%d", &n, &m, &r, &p); + for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); + for(int i = 1, u, v; i < n; ++i) { + scanf("%d%d", &u, &v); + fri[u].push_back(v); + fri[v].push_back(u); + } + dfs1(r, -1); + dfs2(r, r); + while(m--) { + int op, x, y, z; + scanf("%d", &op); + if(op == 1) { + scanf("%d%d%d", &x, &y, &z); + pathadd(x, y, z); + } else if(op ==2) { + scanf("%d%d", &x, &y); + printf("%d\n", query(x, y)); + } else if(op == 3) { + scanf("%d%d", &x, &z); + adds(x, z); + } else { + scanf("%d", &x); + printf("%d\n", querys(x)); + } + } + return 0; +} \ No newline at end of file diff --git a/P3384.md b/P3384.md new file mode 100644 index 0000000..42fd4a3 --- /dev/null +++ b/P3384.md @@ -0,0 +1,74 @@ +# P3384 【模板】重链剖分 / 树链剖分 + +## 题目描述 + +如题,已知一棵包含 $N$ 个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: + +- `1 x y z`,表示将树从 $x$ 到 $y$ 结点最短路径上所有节点的值都加上 $z$。 + +- `2 x y`,表示求树从 $x$ 到 $y$ 结点最短路径上所有节点的值之和。 + +- `3 x z`,表示将以 $x$ 为根节点的子树内所有节点值都加上 $z$。 + +- `4 x`,表示求以 $x$ 为根节点的子树内所有节点值之和。 + +## 输入格式 + +第一行包含 $4$ 个正整数 $N,M,R,P$,分别表示树的结点个数、操作个数、根节点序号和取模数(**即所有的输出结果均对此取模**)。 + +接下来一行包含 $N$ 个非负整数,分别依次表示各个节点上初始的数值。 + +接下来 $N-1$ 行每行包含两个整数 $x,y$,表示点 $x$ 和点 $y$ 之间连有一条边(保证无环且连通)。 + +接下来 $M$ 行每行包含若干个正整数,每行表示一个操作。 + +## 输出格式 + +输出包含若干行,分别依次表示每个操作 $2$ 或操作 $4$ 所得的结果(**对 $P$ 取模**)。 + +## 输入输出样例 #1 + +### 输入 #1 + +``` +5 5 2 24 +7 3 7 8 0 +1 2 +1 5 +3 1 +4 1 +3 4 2 +3 2 2 +4 5 +1 5 1 3 +2 1 3 +``` + +### 输出 #1 + +``` +2 +21 +``` + +## 说明/提示 + +**【数据规模】** + +对于 $30\%$ 的数据: $1 \leq N \leq 10$,$1 \leq M \leq 10$; + +对于 $70\%$ 的数据: $1 \leq N \leq {10}^3$,$1 \leq M \leq {10}^3$; + +对于 $100\%$ 的数据: $1\le N \leq {10}^5$,$1\le M \leq {10}^5$,$1\le R\le N$,$1\le P \le 2^{30}$。所有输入的数均在 `int` 范围内。 + +**【样例说明】** + +树的结构如下: + +![](https://cdn.luogu.com.cn/upload/pic/2319.png) + +各个操作如下: + +![](https://cdn.luogu.com.cn/upload/pic/2320.png) + +故输出应依次为 $2$ 和 $21$。 \ No newline at end of file