【GZOI2019】旧词
题目链接:https://www.luogu.com.cn/problem/P5305
题目大意:给定一棵有根树,已知常数 \(k\), 对于 \(Q\) 个询问 \(x, y\) 求 \(\sum\limits_{i \leq x} {depth(Lca(i, z))^k}\)
solution
当 \(k = 1\) 时, 显然就是原题【LN2014】LCA
若 \(k \ne 1\) , 则可以进一步推广上一题的算法 , 对于每次修改点到根的权值分别加 1 其实就是对 \(depth\)的一种差分\((depth(i) - depth(father(i)) = 1)\) , 因此从根到节点路径上的权值之和即为节点深度
现在依然可以将 \(depth^k\) 进行差分, 则若点 \(i\) 的深度为\(dep_i\) , 则 \(i\) 的权值每次应该增加 \({dep_i}^k - (dep_i - 1)^k\) , 最后从根节点到节点的路径上的权值之和即为 \({dep_i}^k\) , 可以先处理差分权值在\(dfs\)序区间上的前缀和 , 然后用树剖 + 线段树 , 离线进行修改与维护 , 具体做法同【LN2014】LCA
时间复杂度: \(O(nlog^2n)\)
code
#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
int RR = 1; FF = 0; char CH = getchar();
for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
FF *= RR;
}
inline void file(string str) {
freopen((str + ".in").c_str(), "r", stdin);
freopen((str + ".out").c_str(), "w", stdout);
}
#define mod 998244353
#define int long long
const int N = 1e5 + 10;
int now, fst[N], nxt[N], num[N], dep[N], n, q, k, sum[N];
int p = 1, si, fa[N], tp[N], rev[N], sg[N], size[N], son[N], ans[N];
void add(int u, int v) {
nxt[++now] = fst[u], fst[u] = now, num[now] = v;
nxt[++now] = fst[v], fst[v] = now, num[now] = u;
}
struct Segment_tree{
int val, tag;
}xds[N << 2];
struct Que{
int id, pi, qi;
friend bool operator < (Que ai, Que bi) {
return ai.pi < bi.pi;
}
}qi[N];
void push_up(int pos) {
xds[pos].val = (xds[pos << 1].val + xds[pos << 1 | 1].val) % mod;
}
void Add(int pos, int l, int r, int k) {
xds[pos].val = (xds[pos].val + k * ((sum[r] - sum[l - 1] + mod) % mod) % mod) % mod;
xds[pos].tag = (xds[pos].tag + k) % mod;
}
void push_down(int pos, int l, int r) {
if(xds[pos].tag == 0) return;
int mid = (l + r) >> 1;
Add(pos << 1, l, mid, xds[pos].tag);
Add(pos << 1 | 1, mid + 1, r, xds[pos].tag);
xds[pos].tag = 0;
}
void modify(int pos, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
Add(pos, l, r, 1);
return;
}
push_down(pos, l, r);
int mid = (l + r) >> 1;
if(mid >= ll) modify(pos << 1, l, mid, ll, rr);
if(mid < rr) modify(pos << 1 | 1, mid + 1, r, ll, rr);
push_up(pos);
}
int query(int pos, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) return xds[pos].val;
push_down(pos, l, r);
int mid = (l + r) >> 1, res = 0;
if(mid >= ll) res = (res + query(pos << 1, l, mid, ll, rr)) % mod;
if(mid < rr) res = (res + query(pos << 1 | 1, mid + 1, r, ll, rr)) % mod;
return res;
}
void fst_dfs(int xi) {
size[xi] = 1, dep[xi] = dep[fa[xi]] + 1;
for(int i = fst[xi]; i; i = nxt[i])
if(num[i] != fa[xi]) {
fst_dfs(num[i]);
size[xi] += size[num[i]];
if(size[num[i]] > size[son[xi]])
son[xi] = num[i];
}
}
void nxt_dfs(int xi) {
if(son[xi]) {
sg[++si] = son[xi], rev[son[xi]] = si;
tp[son[xi]] = tp[xi], nxt_dfs(son[xi]);
}
for(int i = fst[xi]; i; i = nxt[i])
if(num[i] != son[xi] && num[i] != fa[xi]) {
sg[++si] = num[i], rev[num[i]] = si;
tp[num[i]] = num[i], nxt_dfs(num[i]);
}
}
int Qpow(int ai, int ki) {
if(ki <= 1) return ki ? ai % mod : 1;
int hi = Qpow(ai, ki / 2); hi = hi * hi % mod;
return ki & 1 ? hi * ai % mod : hi;
}
void modify_list(int xi) {
while(xi) {
modify(1, 1, n, rev[tp[xi]], rev[xi]);
xi = fa[tp[xi]];
}
}
int query_list(int xi) {
int res = 0;
while(xi) {
res = (res + query(1, 1, n, rev[tp[xi]], rev[xi])) % mod;
xi = fa[tp[xi]];
}
return res;
}
signed main() {
//file("");
read(n), read(q), read(k);
for(int i = 2; i <= n; i++)
read(fa[i]), add(fa[i], i);
si = tp[1] = rev[1] = sg[1] = 1;
fst_dfs(1), nxt_dfs(1);
for(int i = 1; i <= n; i++)
sum[i] = ((sum[i - 1] + Qpow(dep[sg[i]], k)) % mod - Qpow(dep[sg[i]] - 1, k) + mod) % mod;
for(int i = 1; i <= q; i++)
read(qi[i].pi), read(qi[i].qi), qi[i].id = i;
sort(qi + 1, qi + q + 1);
//cout << query(1, 1, n, rev[2], rev[2]) << endl;
for(int i = 1; i <= n; i++) {
modify_list(i);
//cout << query(1, 1, n, rev[3], rev[3]) << endl;
while(qi[p].pi == i)
ans[qi[p].id] = query_list(qi[p].qi), p++;
}
for(int i = 1; i <= q; i++) cout << ans[i] << endl;
return 0;
}