bzoj 3444 最后的晚餐 (并查集)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_42369449/article/details/83245168

题目:

n个人排成一排,有m个条件,第i个条件要求ai和bi相邻,求方案数。

【数据范围】
100%的数据,0<n≤500000,1≤Ai,Bi≤n,0≤m≤n,保证没有人自恋。

算法:并查集+组合数

难度:NOIP

题解:

由于要求排成一排,因此如果关系出现了环则无解;而一个位置最多挨着两个,所以deg[x]>3也无解。

那么剩下的就是若干条链和单个点。每一条链的排列方式有两种,而单个点的排列方式只有一种。

方案数=2^n*C(n,n+m)*n!*m!

时间复杂度:O(n)

代码如下:

注意重边的问题(a->b,b->a)。由于保证了ai互不相同,因此可以对每一个ai存一个bi,

每次只有在bi的暗恋对象不为ai时才更新。

#include <bits/stdc++.h>
#define ll long long 
#define N 500005
using namespace std;
const ll mod=989381;
int fa[N];
int findf(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=findf(fa[x]);
}
int w[N],deg[N];
int cnt[N];
/*ll powermod(int x,int y)
{
	ll ret=1;
	x=x%mod;
	while(y)
	{
		if(y%2)
		{
			ret=(ret*x)%mod;
		}
		y=y/2;
		x=(x*x)%mod;
	}
	return ret%mod;
}*/
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++)
	{
		fa[i]=i;
	}
	for(int i = 1;i <= m;i++)
	{
		int a;
		scanf("%d",&a);//必须换行读入
		scanf("%d",&w[a]);
		if(w[w[a]]==a) continue;
		int t1=findf(a);
		int t2=findf(w[a]);
		if(t1==t2||deg[a]>=2||deg[w[a]]>=2)
		{
			puts("0");
			exit(0);
		} 
		fa[t1]=t2;
		deg[a]++,deg[w[a]]++;
	}
	for(int i = 1;i <= n;i++) cnt[findf(i)]++;
	ll ans=1;
	int ct=0;
	int cn=0;
	for(int i = 1;i <= n;i++)
	{
		if(cnt[i])
		{
			if(cnt[i]>=2) cn++;  
			else ct++;
		}
	}
	for(int i = 1;i <= cn+ct;i++)
	{
		ans=((ans%mod)*(i%mod))%mod;
	}
	ll anss=1;
	for(int i = 1;i <= cn;i++)
	{
		anss=((anss%mod)*(2%mod))%mod;//此题用快速幂会爆炸!!! 
	}
	ans=((ans%mod)*(anss%mod))%mod;
	printf("%lld\n",ans%mod);
	return 0 ;
}

猜你喜欢

转载自blog.csdn.net/qq_42369449/article/details/83245168