传送门:[CSP-SJX2019]多叉堆
方法
1.用并查集处理每棵需要连边的树并统计树的大小(如x连y)
f
a
[
x
]
=
y
,
s
i
z
[
y
]
=
s
i
z
[
x
]
+
s
i
z
[
y
]
{fa[x]=y,siz[y]=siz[x]+siz[y]}
fa[x]=y,siz[y]=siz[x]+siz[y].
2.推出公式:
f
[
j
]
=
f
[
j
]
∗
f
[
i
]
∗
C
(
j
i
e
[
s
i
z
[
x
]
]
,
j
i
e
[
s
i
z
[
x
]
+
s
i
z
[
y
]
−
1
]
)
{f[j]=f[j]*f[i]*C(jie[siz[x]],jie[siz[x]+siz[y]-1])}
f[j]=f[j]∗f[i]∗C(jie[siz[x]],jie[siz[x]+siz[y]−1])
(
f
[
i
]
{f[i]}
f[i]表示以i为根的子树的填数种数).
3.提前处理出阶乘并求逆元(推荐用扩欧qwq).
4.注意开
l
o
n
g
l
o
n
g
{longlong}
longlong并更新输入的x和y.
代码
#include<bits/stdc++.h>
#define in read()
#define re register
#define N 300005
#define mod 1000000007
#define int long long
using namespace std;
int n,q,opt,ans=0;
int a[N];
int siz[N],fa[N];
int f[N],jie[N];
int X,Y;
inline int in{
int i=0;char ch;
while(!isdigit(ch)){ch=getchar();}
while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch-'0');ch=getchar();}
return i;
}
inline int exgcd(int a,int b,int &X,int &Y)
{
if(b==0)
{
X=1,Y=0;
return a;
}
int r=exgcd(b,a%b,X,Y);
int t=X;
X=Y;
Y=t-(a/b)*Y;
return r;
}
inline int niyuan(int a,int b)
{
exgcd(a,b,X,Y);
return (X%b+b)%b;
}
inline int getfa(int x)
{
if(x==fa[x])return x;
fa[x]=getfa(fa[x]);
return fa[x];
}
inline void work(int i,int j)
{
i=getfa(i);
j=getfa(j);
fa[i]=j;
f[j]=(f[j]*f[i])%mod;
f[j]=(f[j]*jie[siz[i]+siz[j]-1])%mod;
f[j]=(f[j]*niyuan(jie[siz[i]]*jie[siz[j]-1],mod))%mod;
siz[j]+=siz[i];
return;
}
signed main()
{
n=in,q=in;
fill(jie,jie+n+1,1);
for(re int i=0;i<n;i++)
{
fa[i]=i;
siz[i]=f[i]=1;
}
for(re int i=1;i<=300001;i++)
jie[i]=(jie[i-1]*i)%mod;
while(q--)
{
int x,y;
opt=in;
if(opt==1)
{
x=in,y=in;
x=(x+ans)%n,y=(y+ans)%n;
work(x,y);
}
else
{
x=in;
x=(x+ans)%n;
x=getfa(x);
ans=f[x]%mod;
printf("%lld\n",ans);
}
}
return 0;
}