【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; }