Ural 1962 In Chinese Restaurant

链接:http://acm.timus.ru/problem.aspx?space=1&num=1962

题意:N个人坐大圆桌,1必须坐在1号位置,前M个人要求坐Ki的身边,问有几种安排位置的方式。

题解:其实因为N范围比较小,所以用DFS解是挺快的。但是表示我又脑洞大开写了一个并查集。。

基本思路是将相接触的数字放到一个集合。如果出现环,要判定环是否合法。

搞完出来的结果就是,若干个集合。。其中有1的集合位置确定,剩下的集合可以排列组合,总共N!种。然后对于所有集合,如果一个集合个数大于2,可以调换前后位置,也就是还要*2。

最后注意N=2的情况。。不管怎么都输出1,坑死我了。。

总之就是很多特判的题目,代码奉上:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <map>
#include <set>
using namespace std;
#define LL __int64
#define MOD  1000000007

int n,m,fa[100100];
int hash[100100];

int getfa(int x)
{
	if (fa[x]!=x) return fa[x]=getfa(fa[x]);
	else return x;
} 
void add(int x,int y)
{
	if (x>y) fa[x]=y;
	else fa[y]=x;
}
bool check()
{
	for (int a=1;a<=n;a++)
	if (getfa(a)!=1) return false;
	return true; 
}
int MAP[110][110]; 
int main()
{
	int a,b;
	int P;
	while(~scanf("%d%d",&n,&m))
	{
		if (n==2) 
		{
			for (a=1;a<=m;a++)	scanf("%d",&P);
			printf("1\n");continue;
		}
		for (a=1;a<=n;a++)
		{
			fa[a]=a;
			hash[a]=2;
		}
		memset(MAP,0,sizeof(MAP));
		bool flag=true;
		for (a=1;a<=m;a++)
		{
			scanf("%d",&P);
			int X=getfa(a);
			int Y=getfa(P);
			
			if (MAP[a][P]==1)  continue;
			else 
			{
				MAP[a][P]=1;
				MAP[P][a]=1;
			}
			
			if (X==Y) 
			{
				if (hash[a]==1&&hash[P]==1&&check())
				{
					continue;
				}
				else 
				{
					printf("0\n");
					flag=false;
					break;
				}
			}
			else 
			{
				add(X,Y);
				hash[a]--;
				hash[P]--;
				if (hash[a]<0||hash[P]<0) 
				{
					printf("0\n");
					flag=false;
					break;
				}
			}
		}
		for (a++;a<=m;a++) scanf("%d",&P); 
		if (!flag) continue;
		map <int,int> M;
		M.clear();
		for (a=1;a<=n;a++)
		{
			int X=getfa(a);
			if (M.find(X)!=M.end()) {M[X]+=1;}
			else M[X]=1;
		}
		LL whole=1;
		int num=0;
		map <int,int>::iterator it;
		for (it=M.begin();it!=M.end();it++)
		{
			//cout<<(*it).first<<' '<<(*it).second<<endl;
			if ((*it).first==1) 
			{
				if ((*it).second>1) whole=(whole*2)%MOD;
				else  whole=whole;
			}
		 	else 
			if ((*it).second>1) {num++;whole=(whole*2*num)%MOD;}
			else {num++;whole=(whole*num)%MOD;}
		}
		printf("%I64d\n",whole);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值