题目描述
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;
}