LibreOJ 3088 「GXOI / GZOI2019」旧词

几乎同「『LNOI2014』LCA」……
只不过套了个 \(k\) 次方。

原来点 \(p\) 的贡献是 \(1\),即 \(dep_p - dep_{fa_p}\)
其中 \(dep_p\) 表示 \(p\) 的深度,\(fa_p\) 表示 \(p\) 的父亲。

现在,我们考虑把每个点到根路径上的深度 \(k\) 次方像上面这样差分。
即令点 \(p\) 的贡献为 \(dep_p^k - dep_{fa_p}^k\)
然后在线段树上同时维护这个附加权值即可。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <cstdio>
#include <utility>
#include <algorithm>
using namespace std;
const int N = 5e4;
const int mod = 998244353;
int n,q,k;
inline int fpow(int a,int b)
{
int ret = 1;
for(;b;b >>= 1)
(b & 1) && (ret = (long long)ret * a % mod),a = (long long)a * a % mod;
return ret;
}
int to[N + 5],pre[N + 5],first[N + 5];
inline void add(int u,int v)
{
static int tot = 0;
to[++tot] = v,pre[tot] = first[u],first[u] = tot;
}
int fa[N + 5],dep[N + 5],sz[N + 5],son[N + 5],top[N + 5],id[N + 5],rk[N + 5],a[N + 5];
void dfs1(int p)
{
sz[p] = 1,a[p] = ((fpow(dep[p],k) - fpow(dep[fa[p]],k)) % mod + mod) % mod;
for(register int i = first[p];i;i = pre[i])
if(to[i] ^ fa[p])
{
fa[to[i]] = p,dep[to[i]] = dep[p] + 1,dfs1(to[i]),sz[p] += sz[to[i]];
if(!son[p] || sz[to[i]] > sz[son[p]])
son[p] = to[i];
}
}
void dfs2(int p)
{
static int tot = 0;
rk[id[p] = ++tot] = p;
if(son[p])
top[son[p]] = top[p],dfs2(son[p]);
for(register int i = first[p];i;i = pre[i])
if(!id[to[i]])
top[to[i]] = to[i],dfs2(to[i]);
}
struct node
{
int sum,tag,val;
int ls,rs;
} seg[(N << 7) + 10];
int rt[N + 5],seg_tot;
void build(int &p,int tl,int tr)
{
p = ++seg_tot;
if(tl == tr)
{
seg[p].val = a[rk[tl]];
return ;
}
int mid = tl + tr >> 1;
build(seg[p].ls,tl,mid),build(seg[p].rs,mid + 1,tr);
seg[p].val = (seg[seg[p].ls].val + seg[seg[p].rs].val) % mod;
}
void update(int l,int r,int &p,int tl,int tr)
{
seg[++seg_tot] = seg[p],p = seg_tot;
if(l <= tl && tr <= r)
{
seg[p].sum = (seg[p].sum + seg[p].val) % mod,seg[p].tag = (seg[p].tag + 1) % mod;
return ;
}
int mid = tl + tr >> 1;
l <= mid && (update(l,r,seg[p].ls,tl,mid),1);
r > mid && (update(l,r,seg[p].rs,mid + 1,tr),1);
seg[p].sum = ((seg[seg[p].ls].sum + seg[seg[p].rs].sum) % mod + (long long)seg[p].tag * seg[p].val) % mod;
}
inline pair<int,int> operator+(const pair<int,int> &a,const pair<int,int> &b)
{
return make_pair((a.first + b.first) % mod,(a.second + b.second) % mod);
}
pair<int,int> query(int l,int r,int p,int tl,int tr)
{
if(!p || (l <= tl && tr <= r))
return make_pair(seg[p].sum,seg[p].val);
int mid = tl + tr >> 1;
pair<int,int> ret = make_pair(0,0);
l <= mid && (ret = ret + query(l,r,seg[p].ls,tl,mid),1);
r > mid && (ret = ret + query(l,r,seg[p].rs,mid + 1,tr),1);
ret.first = (ret.first + (long long)ret.second * seg[p].tag) % mod;
return ret;
}
int query(int x,int p)
{
int ret = 0;
while(x)
ret = (ret + query(id[top[x]],id[x],rt[p],1,n).first) % mod,x = fa[top[x]];
return ret;
}
int main()
{
scanf("%d%d%d",&n,&q,&k);
int u;
for(register int i = 2;i <= n;++i)
scanf("%d",&u),add(u,i);
dep[1] = 1,top[1] = 1,dfs1(1),dfs2(1),build(rt[0],1,n);
for(register int i = 1,x;i <= n && (x = i,rt[i] = rt[i - 1],1);++i)
while(x)
update(id[top[x]],id[x],rt[i],1,n),x = fa[top[x]];
int x,y;
while(q--)
{
scanf("%d%d",&x,&y);
printf("%d\n",query(y,x));
}
}