题解 洛谷 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;
}
posted @ 2020-10-24 20:01  Little09  阅读(401)  评论(0)    收藏  举报