【XSY3961】决战圣诞树(dfn序线段树,多项式,生成函数)
题面
题解
设 \(a_i\) 表示最终满意指数为 \(i\) 的方案数,我们考虑求出 \(a_i\) 的生成函数。
那么树上每个点都要维护一个多项式,表示仅对于这个点的 \(a_i\) 的生成函数。
那么题目就可以看成支持以下几个操作:
-
给定 \(v,a\),将节点 \(v\) 的多项式乘上 \((1+x^a)\)。
-
给定 \(v,b,c\),将节点 \(v\) 的多项式乘上 \((1+x^a+x^b)\)。
-
给定 \(v\),将与节点 \(v\) 相邻的节点的多项式都乘上结点 \(v\) 的多项式。
-
给定 \(u,v\),保证 \(u,v\) 有连边,将以 \(u\) 为根时,\(v\) 子树内所有结点的多项式乘上结点 \(u\) 的多项式。
-
给定 \(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\) 的原根:
- 先判断 \(g^{\varphi(P)}\equiv 1\pmod P\),如果不满足则 \(g\) 不是原根。
- 再枚举 \(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
*/