题目链接:http://codeforces.com/problemset/problem/859/E
题目大意:
有$n$个人,$2n$个座位。
给出这$n$个人初始的座位,和他们想坐的座位。
每个人要么坐在原来的位置不动,要么坐到想坐的座位上,但是不能有两个人坐在同一个座位上。
问你合法的安排座位的方案数。
题解:
考虑把每个人看成边,把每个人想坐的位置连起来,显然我们会得到一个个的联通块
我们设某个联通块的边数为$e$,点数为$v$,那么有$e>=v-1$。
$e>v$的时候显然不存在这样的方案,不过数据好像保证了合法,就随意了
$e=v$时有$2$种方案。考虑环上的一条边(环的长度大于$1$),这条边的方法确定后其他边的放法都确定了
$e=v-1$是有$v$种方案,考虑枚举哪个点不选,其他的边的放法都确定了
考虑并查集维护联通块,对每个联通块记录下$siz$它的大小,$mark$表示这个块的种类($0$表示没有环,$1$表示有自环,$2$表示有环但不是环)
当一个联通块有自环的时候方案肯定是固定的,写一个$unit$函数合并两个联通块即可
#include<algorithm> #include<cstring> #include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int N=2e5+15; const int mod=1e9+7; int siz[N],mark[N],fa[N]; inline int read() { char ch=getchar(); int s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}; while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } int find(int x) { if (fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } void unit(int a,int b) { if (a==b) { mark[find(a)]=2;//自环 return; } a=find(a);b=find(b); if (a==b) { mark[a]=1;//非自环的环 return; } fa[a]=b; mark[b]|=mark[a]; siz[b]+=siz[a]; } int main() { int n=read(); for (int i=1;i<=n*2;i++) fa[i]=i,siz[i]=1,mark[i]=0; for (int i=1,a,b;i<=n;i++) { a=read();b=read(); unit(a,b); } ll ans=1; for (int i=1;i<=n*2;i++) if (fa[i]==i) { if (mark[i]==0) ans=1ll*ans*siz[i]%mod;//没环就是树 else if (mark[i]==1) ans=ans*2%mod; } printf("%lld ",ans); return 0; }