题目链接:http://codeforces.com/problemset/problem/859/E
题意:
有n个人,2n个座位。
给出这n个人初始的座位,和他们想坐的座位。
每个人要么坐在原来的位置不动,要么坐到想坐的座位上,但是不能有两个人坐在同一个座位上。
问你合法的安排座位的方案数。
题解:
将2n个座位抽象成2n个点。
对于每个人,从他的初始位置向想坐的位置连一条边。
总答案即为所有连通块答案的乘积。
由于每一个点最多向外连一条边,所以对于每一个连通块只有三种情况:
(1)是一棵树,根节点不自环,且所有边的方向都是由儿子指向父亲。
(2)是一棵树,根节点自环,且所有边的方向都是由儿子指向父亲。
(3)有且只有一个环。
对于这三种情况,可以发现:
(1)方案数 = 连通块大小siz[fa]
(2)方案数 = 1
(3)方案数 = 2
并查集维护一下,最后统计答案即可。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define MAX_N 200005 6 #define MOD 1000000007 7 8 using namespace std; 9 10 int n; 11 int par[MAX_N]; 12 int siz[MAX_N]; 13 bool tag[MAX_N]; 14 bool loop[MAX_N]; 15 long long ans=1; 16 17 void init_union_find() 18 { 19 for(int i=1;i<=(n<<1);i++) 20 { 21 par[i]=i; 22 siz[i]=1; 23 tag[i]=false; 24 loop[i]=false; 25 } 26 } 27 28 int find(int x) 29 { 30 return par[x]==x ? x : par[x]=find(par[x]); 31 } 32 33 void unite(int x,int y) 34 { 35 int px=find(x); 36 int py=find(y); 37 if(px==py) 38 { 39 tag[px]=true; 40 return; 41 } 42 siz[py]+=siz[px]; 43 tag[py]|=tag[px]; 44 par[px]=py; 45 } 46 47 void read() 48 { 49 cin>>n; 50 init_union_find(); 51 int x,y; 52 for(int i=1;i<=n;i++) 53 { 54 cin>>x>>y; 55 unite(x,y); 56 if(x==y) loop[x]=true; 57 } 58 } 59 60 void work() 61 { 62 for(int i=1;i<=(n<<1);i++) 63 { 64 if(find(i)==i) 65 { 66 if(loop[i]) continue; 67 if(tag[i]) ans=(ans<<1ll)%MOD; 68 else ans=ans*siz[i]%MOD; 69 } 70 } 71 cout<<ans<<endl; 72 } 73 74 int main() 75 { 76 read(); 77 work(); 78 }