【题目】
LOJ
给定一棵 n n n个点的树,常数 k k k,以及 Q Q Q个询问,每次询问:
∑ i ≤ x d e p ( l c a ( i , y ) ) k \sum_{i\leq x}dep(lca(i,y))^k i≤x∑dep(lca(i,y))k
答案对 998244353 998244353 998244353取模。
【解题思路】
练一下板子。
离线询问,然后按 x x x从小到大计算贡献,每次相当于到到根路径上分别加权 d e p ( u ) k − d e p ( f a u ) k dep(u)^k-dep(fa_u)^k dep(u)k−dep(fau)k,询问就是查询到根路径和。
树剖+线段树维护即可,复杂度 O ( n log 2 n ) O(n\log ^2 n) O(nlog2n)
不过由于只是到根的,所以求出 dfs \text{dfs} dfs序后搞一搞就一个 log \log log了。
【参考代码】
#include
using namespace std;
const int N=5e4+10,M=N<<2,mod=998244353;
int val[N];
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;
namespace Math
{
int upm(int x){return x>=mod?x-=mod:(x<0?x+mod:x);}
void up(int &x,int y){x=upm(x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}
int qpow(int x,int y){int res=1;for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);return res;}
}
using namespace Math;
namespace Tree
{
int tot,ind;
int head[N],dep[N],top[N],son[N],siz[N],fa[N],pos[N],rem[N];
struct Tway{int v,nex;}e[N];
void add(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
dep[v]=dep[x]+1;dfs1(v);
if(siz[v]>siz[son[x]]) son[x]=v;
}
}
void dfs2(int x,int tp)
{
top[x]=tp;pos[x]=++ind;rem[ind]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].nex) if(e[i].v^son[x]) dfs2(e[i].v,e[i].v);
}
struct Segment
{
#define ls (x<<1)
#define rs (x<<1|1)
int sum[M],tag[M],w[M];
void pushdown(int x)
{
if(!tag[x]) return;
up(sum[ls],mul(w[ls],tag[x]));up(sum[rs],mul(w[rs],tag[x]));
up(tag[ls],tag[x]);up(tag[rs],tag[x]);tag[x]=0;
}
void build(int x,int l,int r)
{
if(l==r){w[x]=val[rem[l]];return;}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
w[x]=upm(w[ls]+w[rs]);
}
void update(int x,int l,int r,int L,int R)
{
if(L<=l && r<=R) {up(sum[x],w[x]);up(tag[x],1);return;}
pushdown(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R);
if(R>mid) update(rs,mid+1,r,L,R);
sum[x]=upm(sum[ls]+sum[rs]);
}
int query(int x,int l,int r,int L,int R)
{
if(L<=l && r<=R) return sum[x];
pushdown(x);
int mid=(l+r)>>1,res=0;
if(L<=mid) up(res,query(ls,l,mid,L,R));
if(R>mid) up(res,query(rs,mid+1,r,L,R));
return res;
}
#undef ls
#undef rs
}T;
}
using namespace Tree;
namespace DreamLolita
{
int n,Q,K,ans[N];
struct Tquery{int x,y,id;}q[N];
bool cmp(const Tquery&A,const Tquery&B){return A.x<B.x;}
void modify(int x,int y)
{
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
T.update(1,1,n,pos[top[x]],pos[x]);x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
T.update(1,1,n,pos[y],pos[x]);
}
int query(int x,int y)
{
int res=0;
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
up(res,T.query(1,1,n,pos[top[x]],pos[x]));x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
up(res,T.query(1,1,n,pos[y],pos[x]));
return res;
}
void solution()
{
n=read();Q=read();K=read();
for(int i=2;i<=n;++i) fa[i]=read(),add(fa[i],i);
dep[1]=1;dfs1(1);dfs2(1,1);
for(int i=1;i<=n;++i) val[i]=upm(qpow(dep[i],K)-qpow(dep[fa[i]],K));
for(int i=1;i<=Q;++i) q[i].x=read(),q[i].y=read(),q[i].id=i;
sort(q+1,q+Q+1,cmp);T.build(1,1,n);
for(int i=1,las=1;i<=n;++i)
{
modify(1,i);
while(q[las].x==i) ans[q[las].id]=query(1,q[las].y),++las;
}
for(int i=1;i<=Q;++i) writeln(ans[i]);
}
}
int main()
{
#ifdef Durant_Lee
freopen("LOJ3088.in","r",stdin);
freopen("LOJ3088.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}