【树链剖分/其它】LOJ3088 「GXOI / GZOI2019」旧词

【题目】
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 ixdep(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)kdep(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;
}

你可能感兴趣的:(Tree-树链剖分)