題目連結: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 }