【GXOI/GZOI2019】旧词(树链剖分)(树状数组)

传送门


题解:

深度的 k k k次方只是一个标志而已,把 L C A LCA LCA深度的 k k k次方换成 L C A LCA LCA的其他什么属性求和也是可以做的。

考虑枚举询问点到根的路径上每个点,考虑它被算多少次,实际上就是 s i z [ i ] − s i z [ p r e ] siz[i]-siz[pre] siz[i]siz[pre]
其中 s i z siz siz是子树中已经激活的点的数量, p r e pre pre是路径上上一个点。

直接HLD,然后对于每个点维护链外的siz*val链上维护这个的和。

然后询问直接跳链,对于链底特殊处理一下即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));return res;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}

cs int N=5e4+7;

int n,Q,k;
int el[N],nxt[N];
inline void adde(int u,int v){
	nxt[v]=el[u],el[u]=v;
}

int fa[N],d[N],in[N],dfn;
int siz[N],son[N],top[N];
void dfs1(int u,int p){
	d[u]=d[p]+1;
	for(int re v=el[u];v;v=nxt[v]){
		dfs1(v,u);siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}++siz[u];
}
void dfs2(int u,int tp){
	top[u]=tp;in[u]=++dfn;
	if(son[u])dfs2(son[u],tp);
	for(int re v=el[u];v;v=nxt[v])
	if(v!=son[u])dfs2(v,v);
}

struct Query{int x,y,id;}q[N];
int t1[N],t2[N];
inline void ad(int *tr,int p,int v){for(;p<=n;p+=p&-p)Inc(tr[p],v);}
inline int qy(int *tr,int p){int t=0;for(;p;p&=p-1)Inc(t,tr[p]);return t;}

int las[N];
inline void ins(int u){
	for(;u;u=fa[top[u]]){
		ad(t1,in[top[u]],1),ad(t1,in[u]+1,mod-1);
		int tmp=mul(d[u],dec(qy(t1,in[u]),qy(t1,in[son[u]])));
		ad(t2,in[u],dec(tmp,las[u]));las[u]=tmp;
	}
}

inline int qry(int u){
	int res=0,pre=0;
	for(;u;u=fa[top[u]]){
		Inc(res,mul(d[u],dec(qy(t1,in[u]),qy(t1,in[pre]))));
		if(u!=top[u]){
			u=fa[u];
			Inc(res,qy(t2,in[u]));
			Dec(res,qy(t2,in[top[u]]-1));
		}
		pre=top[u];
	}
	return res;
}

int ans[N];
signed main(){
#ifdef zxyoi
	freopen("poem.in","r",stdin);
#endif
	n=gi(),Q=gi(),k=gi()%(mod-1);
	for(int re i=2;i<=n;++i)adde(fa[i]=gi(),i);
	dfs1(1,0),dfs2(1,1);
	for(int re i=1;i<=n;++i)d[i]=power(d[i],k);
	for(int re i=1;i<=Q;++i)q[i].x=gi(),q[i].y=gi(),q[i].id=i;
	std::sort(q+1,q+Q+1,[](cs Query &a,cs Query &b){return a.x<b.x;});
	for(int re i=1,x=0;i<=Q;++i){
		while(x<q[i].x)ins(++x);
		ans[q[i].id]=qry(q[i].y);
	}
	for(int re i=1;i<=Q;++i)cout<<ans[i]<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值