题意:给一棵树,
次询问,每次询问给出
,求
当
的时候它几乎就是[LNOI2014]LCA。
的做法:
的深度可以转化为:将 到根的路径上每个点点权都 ,然后求 到根的路径上的点权和。这个显然可以树剖+线段树解决。所以我们建立可持久化线段树,第 棵线段树表示将 到 的每个节点到根的路径上的点权都加了 的情况,于是可以在线处理询问,只要在第 棵树上求 到根的路径点权和即可。
满分做法:
可以转化为:对于
到根的路径上的点
,将
的点权增加
,这样一来从
到根的路径上的点权总和就增加了
达到我们的目的了。特别地,可以验证当
时
,与前面的做法是一致的。所以这里我们的线段树要做的不再是给区间的每个元素加同一个值,而是一段区间中每个元素要加的值固定且不同。实现这个并不困难,预处理出每个位置要加多少以及它的前缀和即可。不明白可以看代码。
以上用可持久化线段树实现的是在线做法,空间略大。本题不强制在线,可以将询问离线后按 排序,只要维护一棵线段树,从小到大依次加入点即可。
#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
template <typename T> inline void read(T& x) {
int f = 0, c = getchar(); x = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }
typedef long long LL;
const LL mod = 998244353;
const int maxn = 5e4 + 207;
inline LL qpow(LL x, LL k) {
LL s = 1;
for (; k; x = x * x % mod, k >>= 1)
if (k & 1) s = s * x % mod;
return s;
}
int v[maxn], head[maxn], next[maxn], etot;
int dep[maxn], top[maxn], fa[maxn], size[maxn], son[maxn], dfn[maxn], arcdfn[maxn], xys;
LL s[maxn], a[maxn];
int n, q, K;
inline void ae(int x, int y) {
v[++etot] = y; next[etot] = head[x]; head[x] = etot;
}
void dfs(int x) {
dep[x] = dep[fa[x]] + 1; size[x] = 1;
for (int i = head[x]; i; i = next[i]) {
fa[v[i]] = x;
dfs(v[i]);
size[x] += size[v[i]];
if (size[v[i]] >= size[son[x]]) son[x] = v[i];
}
}
void dfs(int x, int t) {
top[x] = t; dfn[x] = ++xys; arcdfn[xys] = x;
if (son[x]) dfs(son[x], t);
for (int i = head[x]; i; i = next[i])
if (v[i] != son[x]) dfs(v[i], v[i]);
}
struct Node {
int lc, rc;
LL sum, add;
Node() : lc(0), rc(0), sum(0), add(0) {}
};
Node T[maxn << 7];
int root[maxn], tot;
inline void update(int o) {
T[o].sum = (T[T[o].lc].sum + T[T[o].rc].sum) % mod;
}
inline void pushdown(int o, int l, int mid, int r) {
if (T[o].add) {
LL &d = T[o].add;
T[++tot] = T[T[o].lc];
T[o].lc = tot;
T[++tot] = T[T[o].rc];
T[o].rc = tot;
T[T[o].lc].sum = (T[T[o].lc].sum + d * (s[mid] - s[l - 1] + mod) % mod) % mod;
T[T[o].rc].sum = (T[T[o].rc].sum + d * (s[r] - s[mid] + mod) % mod) % mod;
T[T[o].lc].add += d;
T[T[o].rc].add += d;
d = 0;
}
}
void modify(int &o, int lb, int rb, int l, int r) {
if (l > rb || r < lb) return;
T[++tot] = T[o]; o = tot;
if (l <= lb && r >= rb) {
T[o].sum = (T[o].sum + s[rb] - s[lb - 1] + mod) % mod;
++T[o].add;
return;
}
int mid = (lb + rb) >> 1;
pushdown(o, lb, mid, rb);
modify(T[o].lc, lb, mid, l, r);
modify(T[o].rc, mid + 1, rb, l, r);
update(o);
}
LL query(int o, int lb, int rb, int l, int r) {
if (!o || l > rb || r < lb) return 0;
if (l <= lb && r >= rb) return T[o].sum;
int mid = (lb + rb) >> 1;
pushdown(o, lb, mid, rb);
return (query(T[o].lc, lb, mid, l, r) + query(T[o].rc, mid + 1, rb, l, r)) % mod;
}
inline void add(int x, int i) {
for (; x; x = fa[top[x]])
modify(root[i], 1, n, dfn[top[x]], dfn[x]);
}
inline LL query(int x, int i) {
LL ans = 0;
for (; x; x = fa[top[x]])
ans = (ans + query(root[i], 1, n, dfn[top[x]], dfn[x])) % mod;
return ans;
}
int main() {
read(n, q, K);
for (int i = 2, f; i <= n; ++i)
read(f), ae(f, i);
dfs(1); dfs(1, 1);
for (int i = 1; i <= n; ++i)
a[i] = (qpow(dep[i], K) - qpow(dep[i] - 1, K) + mod) % mod;
for (int i = 1; i <= n; ++i)
s[i] = (s[i - 1] + a[arcdfn[i]]) % mod;
for (int i = 1; i <= n; ++i) {
root[i] = root[i - 1];
add(i, i);
}
while (q--) {
int x, y; read(x, y);
writeln(query(y, x));
}
return 0;
}