[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)

[GXOI/GZOI2019]旧词 (树链剖分)

题面

给定一棵 \(n\)个点的有根树,节点标号 \([1,n]\),1号节点为根。
给定常数\(k\)
给定\(Q\)个询问,每次询问给定\(x,y\),求:\(\sum_{i=1}^x \mathrm{deep}_{\mathrm{lca}(i,y)}^k \mod 998244353\)

分析

此题为[BZOJ3626] [LNOI2014]LCA(树链剖分)的加强版。

考虑原来的做法(k=1):我们把i到根的路径上所有点+1,y到根路径上的权值和就是lca深度。如果有多个点i,那么权值和就是深度之和。离线再线段树维护即可。

如果把1改成k,类比原来把\(deep[i]\)拆成\(deep[i]\)个1,我们可以把\(deep[i]^k\)拆成\(deep[i]^{k}-(deep[i]-1)^k,(deep[i]-1)^k-(deep[i]-2)^k \dots\)等.这样问题就变成了用线段树维护一个序列\(s\)支持:

  1. \(i \in [l,r]\),\(s_i\)加上定值\(val[i]=deep[i]^k-(deep[i]-1)^k\)
  2. \([l,r]\)的区间和

每个线段树区间维护一个变量delta记录区间内val之和,可以在建树的时候求出。

然后用一个懒标记addm记录每个区间被整体增加的次数,下推的时候加上addm*对应的val之和

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 200000
#define mod 998244353
using namespace std;
typedef long long ll;
inline void qread(int &x) {
	x=0;
	int sign=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	x=x*sign;
}
inline void qprint(ll x) {
	if(x<0) {
		putchar('-');
		qprint(-x);
	} else if(x==0) {
		putchar('0');
		return;
	} else {
		if(x>=10) qprint(x/10);
		putchar('0'+x%10);
	}
}

inline ll fast_pow(ll x,ll k) {
	ll ans=1;
	while(k) {
		if(k&1) ans=ans*x%mod;
		x=x*x%mod;
		k>>=1;
	}
	return ans;
}

int n,m,k;
struct edge {
	int from;
	int to;
	int next;
} E[maxn*2+5];
int esz=1;
int head[maxn+5];
void add_edge(int u,int v) {
	esz++;
	E[esz].from=u;
	E[esz].to=v;
	E[esz].next=head[u];
	head[u]=esz;
}


int deep[maxn+5],sz[maxn+5],fa[maxn+5],son[maxn+5],top[maxn+5],dfn[maxn+5],hash_dfn[maxn+5];
void dfs1(int x,int f) {
	fa[x]=f;
	sz[x]=1;
	deep[x]=deep[f]+1;
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=f) {
			dfs1(y,x);
			sz[x]+=sz[y];
			if(sz[son[x]]<sz[y]) son[x]=y;
		}
	}
}
int tim;
void dfs2(int x,int t) {
	dfn[x]=++tim;
	hash_dfn[dfn[x]]=x;
	top[x]=t;
	if(son[x]) dfs2(son[x],t);
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa[x]&&y!=son[x]) {
			dfs2(y,y);
		}
	}
}
int lca(int x,int y) {
	while(top[x]!=top[y]) {
		if(deep[top[x]]>deep[top[y]]) x=fa[top[x]];
		else y=fa[top[y]];
	}
	if(deep[x]<deep[y]) return x;
	else return y;
}





struct query {
	int x;
	int y;
	ll ans;
	friend bool operator < (query p,query q) {
		return p.x<q.x;
	}
} q[maxn+5];
vector<int>id[maxn+5];
ll val[maxn+5];//预处理每个点的贡献
struct segment_tree {
	struct node {
		int l;
		int r;
		ll sum;
		ll delta;//每次计算要加上的贡献,等于sum(val[i]) (l<=i<=r) 
		ll addm;//记录每个区间的贡献被计算的次数 
		int len() {
			return r-l+1;
		}
	} tree[maxn*4+5];
	void push_up(int pos) {
		tree[pos].sum=(tree[pos<<1].sum+tree[pos<<1|1].sum)%mod;
		tree[pos].delta=(tree[pos<<1].delta+tree[pos<<1|1].delta)%mod;
	}
	void build(int l,int r,ll *a,int pos) {
		tree[pos].l=l;
		tree[pos].r=r;
		if(l==r){
			tree[pos].delta=a[hash_dfn[l]];
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,a,pos<<1);
		build(mid+1,r,a,pos<<1|1);
		push_up(pos);
	}
	void add_tag(int x,int mark) {
		tree[x].sum+=mark*tree[x].delta%mod;
		tree[x].sum%=mod;
		tree[x].addm+=mark;
		tree[x].addm%=mod;
	}
	void push_down(int x) {
		if(tree[x].addm) {
			add_tag(x<<1,tree[x].addm);
			add_tag(x<<1|1,tree[x].addm);
			tree[x].addm=0;
		}
	}
	void update(int L,int R,int val,int pos) {
		if(L<=tree[pos].l&&R>=tree[pos].r) {
			add_tag(pos,val);
			return;
		}
		push_down(pos);
		int mid=(tree[pos].l+tree[pos].r)>>1;
		if(L<=mid) update(L,R,val,pos<<1);
		if(R>mid) update(L,R,val,pos<<1|1);
		push_up(pos);
	}
	ll query(int L,int R,int pos) {
		if(L<=tree[pos].l&&R>=tree[pos].r) {
			return tree[pos].sum;
		}
		push_down(pos);
		int mid=(tree[pos].l+tree[pos].r)>>1;
		ll ans=0;
		if(L<=mid) ans+=query(L,R,pos<<1);
		if(R>mid) ans+=query(L,R,pos<<1|1);
		return ans;
	}
} T;

void update(int x,int y) {
	int tx=top[x],ty=top[y];
	while(tx!=ty) {
		if(deep[tx]<deep[ty]) {
			swap(x,y);
			swap(tx,ty);
		}
		T.update(dfn[tx],dfn[x],1,1);
		x=fa[tx];
		tx=top[x];
	}
	if(deep[x]>deep[y]) swap(x,y);
	T.update(dfn[x],dfn[y],1,1);
}
ll query(int x,int y) {
	ll ans=0;
	int tx=top[x],ty=top[y];
	while(tx!=ty) {
		if(deep[tx]<deep[ty]) {
			swap(x,y);
			swap(tx,ty);
		}
		ans+=T.query(dfn[tx],dfn[x],1);
		x=fa[tx];
		tx=top[x];
	}
	if(deep[x]>deep[y]) swap(x,y);
	ans+=T.query(dfn[x],dfn[y],1);
	return ans;
}


int main() {
	int f;
	qread(n);
	qread(m);
	qread(k);
	for(int i=2; i<=n; i++) {
		qread(f);
		add_edge(i,f);
		add_edge(f,i);
	}
	dfs1(1,0);
	dfs2(1,1);
	for(int i=1; i<=m; i++) {
		qread(q[i].x);
		qread(q[i].y);
		id[q[i].x].push_back(i);
	}
	for(int i=1;i<=n;i++){
		val[i]=(fast_pow(deep[i],k)-fast_pow(deep[i]-1,k)+mod)%mod;
	}
	T.build(1,n,val,1);
	for(int i=1; i<=n; i++) {
		update(1,i);
		for(int j=0; j<(int)id[i].size(); j++) {
			int num=id[i][j];
			q[num].ans=query(1,q[num].y)%mod;
		}
	}
	for(int i=1; i<=m; i++) {
		qprint(q[i].ans);
		putchar('\n');
	}
}

/*
5 5 1
1
4
1
2
4 3
5 4
2 5
1 2
3 2
*/
posted @ 2019-12-26 20:05  birchtree  阅读(160)  评论(0编辑  收藏  举报