【bzoj3444】最后的晚餐 并查集

题目描述

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

输入

输入有m+1行,第一行有两个用空格隔开的正整数n、m,如题所示。接下来的m行,每一行有两个用空格隔开的正整数,第i行为Ai和Bi,表示Ai的暗恋对象为Bi,保证Ai互不相等。

输出

输出只有一行,这一行只有一个数字,如题所示。

样例输入

4 2
1 2
4 3

样例输出

8


题解

并查集

由于排成一排,因此如果关系出现了环则无解;而一个位置最多挨着两个,所以一个点的度数大于3也无解。

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

然后再乘上把它们放到一起的全排列即为答案。

可以使用并查集来维护连通性、判环。

注意重边的问题(a->b,b->a)。由于保证了ai互不相同,因此可以对每一个ai存一个bi,每次只有在bi的暗恋对象不为ai时才更新。

时间复杂度$O(n·\alpha(n))$

#include <cstdio>
#include <algorithm>
#define N 500010
#define mod 989381
using namespace std;
int a[N] , f[N] , d[N] , cnt[N] , tot;
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
	int n , m , i , x;
	long long ans = 1;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) f[i] = i;
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d" , &x);
		scanf("%d" , &a[x]);
		if(a[a[x]] == x) continue;
		if(find(x) == find(a[x]) || d[x] >= 2 || d[a[x]] >= 2)
		{
			puts("0");
			return 0;
		}
		d[x] ++ , d[a[x]] ++ , f[f[x]] = f[a[x]];
	}
	for(i = 1 ; i <= n ; i ++ ) cnt[find(i)] ++ ;
	for(i = 1 ; i <= n ; i ++ )
	{
		if(cnt[i])
		{
			if(cnt[i] > 1) ans = ans * 2 % mod;
			tot ++ ;
		}
	}
	while(tot) ans = ans * tot % mod , tot -- ;
	printf("%lld\n" , ans);
	return 0;
}

 

 

posted @ 2017-09-27 20:24  GXZlegend  阅读(427)  评论(0编辑  收藏  举报