[GXOI/GZOI2019]舊詞
阿新 • • 發佈:2021-10-08
\(\text{Solution}\)
第一部分參考 \(\text{LNOI2014 LCA}\)
在 \(k=1\) 時完全可行
因為對於每個 \(i\), 根到 \(y\) 的路徑之和恰好是 \(dep[lca]\)
但當 \(k>1\) 呢?
此時我們要想辦法弄出一個加數的方式,使根到 \(y\) 的路徑之和為 \(dep[lca]^k\)
考慮每個點 \(x\) 加上的權值為 \(dep[x]^k-dep[fa[x]]^k\)
此時加起來就是 \(dep[lca]^k\)
那問題來了,怎麼快速讓 \(i\) 到根的每個點 \(x\) 加上這種的數
發現對於一個點加上的數總是相同的(即 \(dep[x]^k-dep[fa[x]]^k\)
那麼我們就可以以這個數為點的係數,加 \(x\) 就相當於這個點加上了 \(f \cdot x\)
打一棵不需要修改係數的線段樹即可
\(\text{Code}\)
#include <cstdio> #define LL long long using namespace std; const int N = 5e4 + 5; const LL P = 998244353; int n, q, k, h[N], H[N], tot, Tot; struct edge{int nxt, to;}e[N]; struct node{int nxt, to, id;}Q[N]; inline LL fpow(LL x, LL y) { LL res = 1; for(; y; y >>= 1, x = x * x % P) if (y & 1) res = res * x % P; return res; } int top[N], fa[N], dfn[N], rev[N], siz[N], son[N], dep[N], dfc; void dfs1(int x) { siz[x] = 1; for(int i = h[x]; i; i = e[i].nxt) { int v = e[i].to; dep[v] = dep[x] + 1, dfs1(v), siz[x] += siz[v]; if (siz[son[x]] < siz[v]) son[x] = v; } } void dfs2(int x, int t) { top[x] = t, dfn[x] = ++dfc, rev[dfc] = x; if (son[x]) dfs2(son[x], t); for(int i = h[x]; i; i = e[i].nxt) { int v = e[i].to; if (v == son[x]) continue; dfs2(v, v); } } #define ls (p << 1) #define rs (ls | 1) LL sum[N * 4], f[N * 4], depk[N * 4], tag[N * 4], ans[N]; void build(int p, int l, int r) { if (l == r) return void(f[p] = (depk[rev[l]] - depk[fa[rev[l]]] + P) % P); int mid = (l + r) >> 1; build(ls, l, mid), build(rs, mid + 1, r); f[p] = (f[ls] + f[rs]) % P; } inline void pushdown(int p) { if (!tag[p]) return; sum[ls] = (sum[ls] + tag[p] * f[ls] % P) % P, tag[ls] += tag[p]; sum[rs] = (sum[rs] + tag[p] * f[rs] % P) % P, tag[rs] += tag[p]; tag[p] = 0; } void modify(int p, int l, int r, int tl, int tr, int v) { if (tl <= l && r <= tr) { sum[p] = (sum[p] + f[p] * v % P) % P, tag[p] += v; return; } pushdown(p); int mid = (l + r) >> 1; if (tl <= mid) modify(ls, l, mid, tl, tr, v); if (tr > mid) modify(rs, mid + 1, r, tl, tr, v); sum[p] = (sum[ls] + sum[rs]) % P; } int query(int p, int l, int r, int tl, int tr) { if (tl <= l && r <= tr) return sum[p]; pushdown(p); int mid = (l + r) >> 1; LL res = 0; if (tl <= mid) res = query(ls, l, mid, tl, tr); if (tr > mid) res += query(rs, mid + 1, r, tl, tr); return res % P; } int main() { scanf("%d%d%d", &n, &q, &k); for(int i = 2; i <= n; i++) scanf("%d", &fa[i]), e[++tot] = edge{h[fa[i]], i}, h[fa[i]] = tot; dep[1] = 1, dfs1(1), dfs2(1, 1); for(int i = 1; i <= n; i++) depk[i] = fpow(dep[i], k); build(1, 1, n); for(int i = 1, x, y; i <= q; i++) scanf("%d%d", &x, &y), Q[++Tot] = node{H[x], y, i}, H[x] = Tot; for(int i = 1, x, fx; i <= n; i++) { x = i; for(; x; ) fx = top[x], modify(1, 1, n, dfn[fx], dfn[x], 1), x = fa[fx]; for(int j = H[i]; j; j = Q[j].nxt) { x = Q[j].to; for(; x; ) fx = top[x], ans[Q[j].id] = (ans[Q[j].id] + query(1, 1, n, dfn[fx], dfn[x])) % P, x = fa[fx]; } } for(int i = 1; i <= q; i++) printf("%lld\n", ans[i]); }