【XSY3961】决战圣诞树(dfn序线段树,多项式,生成函数)

题面

决战圣诞树

题解

\(a_i\) 表示最终满意指数为 \(i\) 的方案数,我们考虑求出 \(a_i\) 的生成函数。

那么树上每个点都要维护一个多项式,表示仅对于这个点的 \(a_i\) 的生成函数。

那么题目就可以看成支持以下几个操作:

  1. 给定 \(v,a\),将节点 \(v\) 的多项式乘上 \((1+x^a)\)

  2. 给定 \(v,b,c\),将节点 \(v\) 的多项式乘上 \((1+x^a+x^b)\)

  3. 给定 \(v\),将与节点 \(v\) 相邻的节点的多项式都乘上结点 \(v\) 的多项式。

  4. 给定 \(u,v\),保证 \(u,v\) 有连边,将以 \(u\) 为根时,\(v\) 子树内所有结点的多项式乘上结点 \(u\) 的多项式。

  5. 给定 \(u,v,s\),保证 \(u,v\) 有连边,断开这条边,分别求分成的两棵子树中结点的多项式乘积在模 \((x^K-1)\) 意义下的 \(s\) 次项系数。答案对素数 \(P\) 取模。

    补充知识:为什么模 \((x^K-1)\) 就是对的?

    我们求出多项式乘积后,根据题目要求,要把 \(x^m\)\(m\geq K\))的系数加到 \(x^{m\bmod K}\) 上。而用大除法验证可知模 \((x^K-1)\) 就能实现这个过程。

保证 \(P\bmod K=1\)

考虑类似 FFT 的过程维护两个多项式的循环卷积。

我们找到模 \(P\) 意义下的 \(K\) 次单位根。具体来说,先找到 \(P\) 的原根 \(g\),然后 \(g'=g^{\frac{P-1}{K}}\) 就是满足要求的数。

补充知识:如何找 \(P\) 的原根 \(g\)

我们先求出 \(\varphi(P)\),然后对其分解质因数 \(\varphi(P)=p_1^{a_1}p_2^{a_2}\cdots p_k^{a_k}\)

然后我们枚举 \(g\),判断 \(g\) 是否是 \(P\) 的原根:

  1. 先判断 \(g^{\varphi(P)}\equiv 1\pmod P\),如果不满足则 \(g\) 不是原根。
  2. 再枚举 \(i\),判断 \(g^{\frac{\varphi(P)}{p_i}}\equiv 1\pmod P\),如果满足则 \(g\) 不是原根。

然后,如果我们要求 \(A(x)\)\(B(x)\) 的乘积 \(C(x)\) 在模 \((x^K-1)\) 意义下的系数(系数模 \(P\)),我们就类似 FFT,先将 \(g'^0,g'^1,g'^2,\cdots,g'^{K-1}\) 分别代入 \(A(x)\)\(B(x)\) 求值,得到 \(A(x)\)\(B(x)\) 的点值表达式。然后把它们对应的点值乘起来,得到 \(C(x)\) 的点值表达式。然后再代入 \(g'^{-0},g'^{-1},g'^{-2},\cdots,g'^{-(K-1)}\) 求值,再除个 \(K\) 就得到 \(C(x)\) 的系数表达式了。(注意这个过程后得到的 \(C(x)\) 已经模过 \((x^K-1)\) 了)

那我们不妨只维护每个点的多项式的点值表达式,然后询问的时候再转回系数表达式。

这样做的好处是两个多项式相乘只需要 \(O(K)\)

而由于操作 1 和 2 要乘的多项式的项数很少,我们可以直接暴力代入 \(K\) 个点值。

由于询问的是乘出来的多项式的第 \(s\) 项的系数,所以点值表达式转系数表达式时不一定要把全部系数都求出来,只需要 \(O(K)\) 求出 \(x^s\) 的系数即可。

而对于操作 3 和 4,我们既需要维护子树信息,又需要维护与一个点直接相连的点的信息。

考虑将 dfs 序和 bfs 序结合起来,我们尝试如下标号方式:一开始先把根标号 \(1\),然后 dfs。当 dfs 到某一个节点 \(u\) 时,我们先将 \(u\) 的儿子按顺序标号,然后再对于每一个儿子进去 dfs。不难发现这种方式既能保证一个点的后代标号连续,又能保证一个点的儿子标号连续。

然后就可以线段树维护多项式(点值)乘积了。

小 trick:

注意到线段树是需要维护区间乘、区间乘积的。那么如果某个长度为 \(l\) 的区间里面的每一个数都要乘上 \(a\)\(0\leq a<P\)),这个区间的区间乘积就要乘上 \(a^l\)。如果每次都用快速幂的话,单词修改的复杂度就会变成 \(O(K\log ^2n)\),所以考虑 \(O(1)\)\(a^l\)

由于我们具体要求的是 \(a^l \bmod P\) 的值,而我们又已经求出了 \(P\) 的原根 \(g\)。注意到 \(g^0,g^1,\cdots,g^{\varphi(P)-1}\) 的值和与 \(P\) 互质的 \(\varphi(P)\) 个值是一一对应的。那么当 \(P\) 为质数时,\(g^0,g^1,\cdots,g^{P-2}\) 的值和 \(1\sim P-1\) 是一一对应的。

那么不妨设 \(a=g^b\)\(a=0\) 时特判),那么我们要求的是 \(a^l=g^{bl}\)\(P\) 的值,又由欧拉定理知 \(g^{bl}\equiv g^{bl\bmod \varphi(P)}\equiv g^{bl\bmod (P-1)}\pmod P\)

所以我们可以预处理出当 \(g^{0\sim P-2}\) 的值,同时预处理出 \(1\sim P-1\) 对应的是 \(g\) 的几次方,就可以在线 \(O(1)\)\(a^l\) 了。

总时间复杂度 \(O((n+q)K\log n+P)\)

代码比较长也比较难调,幸亏良(凉)心出题人给了个很好的大样例。

代码如下:

#include<bits/stdc++.h>

#define MK 210
#define N 5010
#define P 2000010

using namespace std;

namespace modular
{
	int mod;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

struct data
{
	int val[MK];
	data(){memset(val,0,sizeof(val));}
}all1,sum[N<<2],lazy[N<<2];

int T,K,invK;
int n,q,datatype;
int g1,g2,p[10],g[MK],powg[P],mp[P];
int cnt,head[N],to[N<<1],nxt[N<<1];
int idx,dfn[N],rk[N],sonl[N],sonr[N];
int fa[N],size[N];
int ans[2];
bool tag[N<<2];

int getphi(int n)
{
	int ans=n,t=sqrt(n);
	for(int i=2;i<=t;i++)
	{
		if(n%i) continue;
		ans=ans/i*(i-1);
		while(!(n%i)) n/=i;
	}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}

void getg()
{
	int phi=getphi(mod);
	int tmp=phi,t=sqrt(phi);
	for(int i=2;i<=t;i++)
	{
		if(!(tmp%i))
		{
			p[++p[0]]=i;
			while(!(tmp%i)) tmp/=i;
		}
	}
	for(g1=2;;g1++)
	{
		if(poww(g1,phi)!=1) continue;
		bool flag=1;
		for(int i=1;i<=p[0];i++)
		{
			if(poww(g1,phi/p[i])==1)
			{
				flag=0;
				break;
			}
		}
		if(flag) break;
	}
}

void init()
{
	invK=poww(K,mod-2);
	for(int i=0;i<K;i++) all1.val[i]=1;
	getg();
	g2=poww(g1,(mod-1)/K);
	g[0]=1;
	for(int i=1;i<K;i++) g[i]=mul(g[i-1],g2);
	powg[0]=1,mp[powg[0]]=0;
	for(int i=1;i<mod-1;i++)
	{
		powg[i]=mul(powg[i-1],g1);
		mp[powg[i]]=i;
	}
}

void adde(int u,int v)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

void dfs(int u)
{
	size[u]=1;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==fa[u]) continue;
		fa[v]=u;
		dfn[v]=++idx,rk[idx]=v;
		if(!sonl[u]) sonl[u]=dfn[v];
		sonr[u]=dfn[v];
	}
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa[u])
		{
			dfs(v);
			size[u]+=size[v];
		}
	}
}

void up(int k)
{
	for(int i=0;i<K;i++)
		sum[k].val[i]=mul(sum[k<<1].val[i],sum[k<<1|1].val[i]);
}

void downn(int k,int len,data now)
{
	for(int i=0;i<K;i++)
	{
		if(!now.val[i])
		{
			sum[k].val[i]=lazy[k].val[i]=0;
			continue;
		}
		int tmp=1ll*mp[now.val[i]]*len%(mod-1);
		sum[k].val[i]=mul(sum[k].val[i],powg[tmp]);
		lazy[k].val[i]=mul(lazy[k].val[i],now.val[i]);
	}
	tag[k]=1;
}

void down(int k,int l,int r,int mid)
{
	if(tag[k])
	{
		downn(k<<1,mid-l+1,lazy[k]),downn(k<<1|1,r-mid,lazy[k]);
		tag[k]=0,lazy[k]=all1;
	}
}

void build(int k,int l,int r)
{
	tag[k]=0,sum[k]=lazy[k]=all1;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void update1(int k,int l,int r,int x,data now)
{
	if(l==r)
	{
		for(int i=0;i<K;i++) 
			sum[k].val[i]=mul(sum[k].val[i],now.val[i]);
		return;
	}
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	if(x<=mid) update1(k<<1,l,mid,x,now);
	else update1(k<<1|1,mid+1,r,x,now);
	up(k);
}

void update2(int k,int l,int r,int ql,int qr,data now)
{
	if(ql<=l&&r<=qr)
	{
		downn(k,r-l+1,now);
		return;
	}
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	if(ql<=mid) update2(k<<1,l,mid,ql,qr,now);
	if(qr>mid) update2(k<<1|1,mid+1,r,ql,qr,now);
	up(k);
}

data query1(int k,int l,int r,int x)
{
	if(l==r) return sum[k];
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	if(x<=mid) return query1(k<<1,l,mid,x);
	return query1(k<<1|1,mid+1,r,x);
}

data query2(int k,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr) return sum[k];
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	data ans=all1;
	if(ql<=mid)
	{
		data now=query2(k<<1,l,mid,ql,qr);
		for(int i=0;i<K;i++)
			ans.val[i]=mul(ans.val[i],now.val[i]);
	}
	if(qr>mid)
	{
		data now=query2(k<<1|1,mid+1,r,ql,qr);
		for(int i=0;i<K;i++)
			ans.val[i]=mul(ans.val[i],now.val[i]);
	}
	return ans;
}

int main()
{
	T=read(),K=read(),mod=read();
	init();
	while(T--)
	{
		n=read(),q=read(),datatype=read();
		cnt=idx=0;
		for(int i=1;i<=n;i++) head[i]=sonl[i]=0;
		for(int i=1;i<n;i++)
		{
			int u=read(),v=read();
			adde(u,v),adde(v,u);
		}
		dfn[1]=++idx,rk[idx]=1;
		dfs(1);
		build(1,1,n);
		while(q--)
		{
			char opt[1];
			scanf("%s",opt);
			if(opt[0]=='L')
			{
				int u=read(),a=read();
				data now;
				for(int i=0;i<K;i++)
					now.val[i]=add(1,poww(g[i],a));
				update1(1,1,n,dfn[u],now);
			}
			if(opt[0]=='C')
			{
				int u=read(),a=read(),b=read();
				data now;
				for(int i=0;i<K;i++)
					now.val[i]=add(1,add(poww(g[i],a),poww(g[i],b)));
				update1(1,1,n,dfn[u],now);
			}
			if(opt[0]=='F')
			{
				int u=read();
				data now=query1(1,1,n,dfn[u]);
				if(fa[u]) update1(1,1,n,dfn[fa[u]],now);
				if(sonl[u]) update2(1,1,n,sonl[u],sonr[u],now);
			}
			if(opt[0]=='S')
			{
				int u=read(),v=read();
				data now=query1(1,1,n,dfn[u]);
				if(v==fa[u])
				{
					if(1<=dfn[u]-1) update2(1,1,n,1,dfn[u]-1,now);
					if(sonl[u]&&dfn[u]+1<=sonl[u]-1) update2(1,1,n,dfn[u]+1,sonl[u]-1,now);
					if(sonl[u])
					{
						if(sonl[u]+size[u]-1<=n) update2(1,1,n,sonl[u]+size[u]-1,n,now);
					}
					else 
					{
						if(dfn[u]+1<=n) update2(1,1,n,dfn[u]+1,n,now);
					}
				}
				else
				{
					update1(1,1,n,dfn[v],now);
					if(sonl[v]) update2(1,1,n,sonl[v],sonl[v]+size[v]-2,now);
				}
			}
			if(opt[0]=='Q')
			{
				int u=read(),v=read(),s=read();
				bool rev=0;
				if(v==fa[u]) swap(u,v),rev=1;
				data now1=(1<=dfn[v]-1?query2(1,1,n,1,dfn[v]-1):all1);
				data now2=(sonl[v]&&dfn[v]+1<=sonl[v]-1?query2(1,1,n,dfn[v]+1,sonl[v]-1):all1);
				data now3;
				if(sonl[v]) now3=(sonl[v]+size[v]-1<=n?query2(1,1,n,sonl[v]+size[v]-1,n):all1);
				else now3=(dfn[v]+1<=n?query2(1,1,n,dfn[v]+1,n):all1);
				data now;
				int tmp=1;
				ans[0]=0;
				for(int i=0;i<K;tmp=mul(tmp,g[s?K-s:s]),i++)
				{
					now.val[i]=mul(now1.val[i],mul(now2.val[i],now3.val[i]));
					ans[0]=add(ans[0],mul(now.val[i],tmp));
				}
				ans[0]=mul(ans[0],invK);
				
				now1=query1(1,1,n,dfn[v]);
				now2=(sonl[v]?query2(1,1,n,sonl[v],sonl[v]+size[v]-2):all1);
				tmp=1;
				ans[1]=0;
				for(int i=0;i<K;tmp=mul(tmp,g[s?K-s:s]),i++)
				{
					now.val[i]=mul(now1.val[i],now2.val[i]);
					ans[1]=add(ans[1],mul(now.val[i],tmp));
				}
				ans[1]=mul(ans[1],invK);
				printf("%d %d\n",ans[0^rev],ans[1^rev]);
			}
		}
	}
	return 0;
}
/*
1 119 974849
5 17 31
1 2
1 3
2 4
2 5
Q 1 3 0
Q 1 2 54
L 2 24
Q 1 2 24
C 1 20 30
Q 2 4 54
Q 1 2 54
Q 5 2 54
F 2
Q 1 2 24
Q 1 2 48
Q 1 2 72
S 1 3
Q 2 4 24
Q 2 4 48
Q 2 4 54
Q 2 4 72
*/
posted @ 2022-10-30 14:26  ez_lcw  阅读(34)  评论(0编辑  收藏  举报