【GXOI\GZOI2019】旧词【差分】【树剖】

嗯,,这道题和上道题一样的

上道题是这道题的递归题。

如果明白上道题的处理方式,这道题就差不多了 。

唯一的难点在于k次方,如何维护?

如果我们仍然将1到某个点权值都+1,还要满足这对该点子树贡献是该点深度的k次方。

我们可以继续使用差分思想。将第i个点赋一个加权权值为其深度的k次方减去(该点深度-1)的k次方。

然后就可以继续快乐的维护了。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
const int mod=998244353;
struct node{
	int l,r,a,b,tag,sum;
}t[200003];
struct bili{
	int belong,pos,flag,z;
}que[200003];int ins;
int mi[200003],ans[200003];
int n,q,k;
int first[200003],nxt[200003],to[200003],tot,cnt;
int id[200003],dep[200003],pre[200003],son[200003],fa[200003],top[200003],size[200003];
int ksm(int x,int y){
	int sum=1;
	while(y){
		if(y&1)sum=sum*x%mod;x=x*x%mod;y>>=1;
	}return sum;
}
void dfs1(int u,int faa){
	size[u]=1;fa[u]=faa;dep[u]=dep[faa]+1;
	for(int i=first[u];i;i=nxt[i]){
		int v=to[i];if(v==faa)continue;
		dfs1(v,u);size[u]+=size[v];if(size[v]>size[son[u]])son[u]=v;
	}
}

void dfs2(int u,int tp){
	id[u]=++cnt;pre[cnt]=u;top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int i=first[u];i;i=nxt[i]){
		int v=to[i];if(v==fa[u]||v==son[u])continue;dfs2(v,v);
	} 
}

void add(int a,int b){
	nxt[++tot]=first[a];first[a]=tot;to[tot]=b;
}

void pushup(int u){
	t[u].b=t[u*2].b+t[u*2+1].b;t[u].b%=mod;
	t[u].sum=t[u*2].sum+t[u*2+1].sum;t[u].sum%=mod;
}
void build(int u,int l,int r){
	t[u].l=l;t[u].r=r;if(l==r){t[u].b=(mi[dep[pre[l]]]-mi[dep[pre[l]]-1]+mod)%mod;return;}
	int mid=(l+r)>>1;build(u*2,l,mid);build(u*2+1,mid+1,r);pushup(u);
}

bool cm(bili a,bili b){return a.pos<b.pos;}

void pushdown(int u){
	if(t[u].tag){
		int x=t[u].tag;
		t[u*2].tag+=x;t[u*2].tag%=mod;t[u*2+1].tag+=x;t[u*2+1].tag%=mod;
		t[u*2].sum+=t[u*2].b*x%mod;t[u*2].sum%=mod;t[u*2+1].sum+=t[u*2+1].b*x%mod;t[u*2+1].sum%=mod;
		t[u].tag=0;
	}
}

void segmo(int u,int ql,int qr){
	if(t[u].r<ql||t[u].l>qr)return;
	if(ql<=t[u].l&&t[u].r<=qr){t[u].sum=(t[u].sum+t[u].b)%mod;t[u].tag=(t[u].tag+1)%mod;return;}
	pushdown(u);segmo(u*2,ql,qr);segmo(u*2+1,ql,qr);pushup(u);
}

void modify(int a,int b){
	int x=top[a],y=top[b];
	while(x!=y){
		if(dep[x]<dep[y])swap(x,y),swap(a,b);
		segmo(1,id[x],id[a]);
		a=fa[x];x=top[a];
	}if(id[a]>id[b])swap(a,b);
	segmo(1,id[a],id[b]);
}

int segquery(int u,int ql,int qr){
	if(t[u].r<ql||t[u].l>qr)return 0;
	if(ql<=t[u].l&&t[u].r<=qr)return t[u].sum;
	pushdown(u);return (segquery(u*2,ql,qr)+segquery(u*2+1,ql,qr))%mod;
	pushup(u);
}

int query(int a,int b){
	int x=top[a],y=top[b],sum=0;
	while(x!=y){
		if(dep[x]<dep[y])swap(x,y),swap(a,b);
		sum=(sum+segquery(1,id[x],id[a]))%mod;
		a=fa[x];x=top[a];
	}if(id[a]>id[b])swap(a,b);
	sum=(sum+segquery(1,id[a],id[b]))%mod;
	return sum;
}

void debug1(int u,int l,int r){
	cout<<l<<" "<<r<<" "<<t[u].b<<endl;
	if(l==r)return;
	int mid=(l+r)>>1;debug1(u*2,l,mid);debug1(u*2+1,mid+1,r);
}

void debug2(int u,int l,int r){
	cout<<l<<" "<<r<<" "<<t[u].sum<<endl;
	if(l==r)return;pushdown(u);
	int mid=(l+r)>>1;debug2(u*2,l,mid);debug2(u*2+1,mid+1,r);
} 

signed main(){
	n=in;q=in;k=in;
	mi[0]=1;
	for(int i=1;i<=n;i++)mi[i]=ksm(i,k);mi[0]=0;
	for(int i=2;i<=n;i++){
		int x=in;add(x,i);
	}dfs1(1,0);dfs2(1,1);build(1,1,n);int x,y;
//	for(int i=1;i<=n;i++)cout<<id[i]<<" ";cout<<endl;
	//debug1(1,1,n);
	for(int i=1;i<=q;i++){
		x=in;y=in;
		que[i].pos=x;que[i].z=y;que[i].belong=i;
	}sort(que+1,que+q+1,cm);
	for(int i=1,ri=1;i<=n;i++){
		modify(1,i);//cout<<"#################"<<endl;debug2(1,1,n);
		while(que[ri].pos==i){
			ans[que[ri].belong]=query(1,que[ri].z);ri++;
		}
	}
	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值