题解 洛谷 P5689 【[CSP-SJX2019]多叉堆】
一整棵树的答案很显然是一样的,所以我们可以考虑并查集维护。
我们可以用 \(size_i\) 表示以 \(i\) 为根的子树大小。注意这里,如果 \(i\) 已经是一棵树上的节点,那么就不用维护了。这个显然可以维护:在合并的时候相加即可。
再用 \(ans_i\) 表示以 \(i\) 为根的答案。考虑当把以 \(y\) 为根的树接在以 \(x\) 为根的树下,则 \(ans_x=ans_x*ans_y*C_{size_x+size_y-1}^{size_y}\)。这个很好理解,就是因为 \(1\)这个数只能放在根上,其他 \(size_x+size_y-1\) 个数任选出 \(size_y\) 个数摆在以 \(y\) 为根的树上,其它数摆在以 \(x\) 为根的树上,两个方案相乘即可。
至于组合数怎么求,预处理一下阶乘和阶乘的逆元就行了。
结合代码理解下(不要在意奇怪的变量名比如 \(jc\) 是阶乘的意思)。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[300501];
int size[300001];
long long ans[300001];
const long long mod=1e9+7;
long long jc[300001],njc[300001];
inline long long read()
{
char C;
long long F=1,ANS=0;
while (C<'0'||C>'9')
{
if (C=='-') F=-1;
C=getchar();
}
while (C>='0'&&C<='9')
{
ANS=ANS*10+C-'0';
C=getchar();
}
return F*ANS;
}
long long ksm(long long x,int y)
{
if (y==0) return 1;
long long res=ksm(x,y/2);
res=(res*res)%mod;
if (y%2==0) return res;
return (res*x)%mod;
}
inline long long C(int x,int y)
{
return ((jc[x]*njc[y])%mod)*njc[x-y]%mod;
}
inline int find(int x)
{
if (x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
int main()
{
n=read(),m=read();
for (int i=0;i<n;i++) fa[i]=i,size[i]=1,ans[i]=1;
jc[0]=1;
for (int i=1;i<=n;i++) jc[i]=(jc[i-1]*i)%mod;
njc[n]=ksm(jc[n],mod-2);
for (int i=n-1;i>=0;i--) njc[i]=(njc[i+1]*(i+1))%mod;
long long lastans=0;
for (int i=1;i<=m;i++)
{
int x,y,t;
t=read(),x=read();
x=(x+lastans)%n;
if (t==1)
{
y=read();
y=(y+lastans)%n;
int x1=find(x),y1=find(y);
ans[y1]=((ans[y1]*ans[x1])%mod*C(size[x1]+size[y1]-1,size[x1]))%mod;
size[y1]+=size[x1];
fa[x1]=y1;
}
else
{
lastans=ans[find(x)];
printf("%lld\n",lastans);
}
}
return 0;
}