问题描述
A new set of desks just arrived, and it's about time! Things were getting quite cramped in the office. You've been put in charge of creating a new seating chart for the engineers. The desks are numbered, and you sent out a survey to the engineering team asking each engineer the number of the desk they currently sit at, and the number of the desk they would like to sit at (which may be the same as their current desk). Each engineer must either remain where they sit, or move to the desired seat they indicated in the survey. No two engineers currently sit at the same desk, nor may any two engineers sit at the same desk in the new seating arrangement.
How many seating arrangements can you create that meet the specified requirements? The answer may be very large, so compute it modulo 1000000007 = 109 + 7.
输入格式
Input will begin with a line containing N (1 ≤ N ≤ 100000), the number of engineers.
N lines follow, each containing exactly two integers. The i-th line contains the number of the current desk of the i-th engineer and the number of the desk the i-th engineer wants to move to. Desks are numbered from 1 to 2·N. It is guaranteed that no two engineers sit at the same desk.
输出格式
Print the number of possible assignments, modulo 1000000007 = 109 + 7.
样例输入
4
1 5
5 2
3 7
7 3
样例输出
6
题目大意
有N 个人和2N个座位。告诉你这N个人它们现在的座位。以及它们想去的座位。 每个人可以去它们想去的座位或者就坐在原来的座位上。 新的座位安排和旧的座位安排,都不允许一个座位被两个人占据的情况。 问你新的座位安排的方案数。
解析
很容易想到将给的对应关系当做边建图。那么这个图有什么性质呢?发现这张图有人的点出度都为1,一开始没有人的位置在图中的出度为0。如果将出度为0的点看做一个空格,那么方案数就是所有空格沿反向边移动能够到达的点数量的乘积(相当于题目中的交换位置)。
但还是考虑环的情况。显然一个环只有两种方法(保持原位和都延环移动一步)。而环中没有出度为0的点,也就没有空位,那么Tarjan缩点后虽然环的出度也为0,但只能提供2种方案。注意自环只有保持原位一种方案
代码
#include <iostream>
#include <cstdio>
#define int long long
#define N 200002
using namespace std;
const int mod=1000000007;
int head[N],ver[N*2],nxt[N*2],l;
int head1[N],ver1[N*2],nxt1[N*2],l1;
int n,i,j,tim,dfn[N],low[N],top,s[N],cnt,scc[N],sum[N],num,size[N],d[N],tmp;
bool vis[N],pos[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y)
{
l++;
ver[l]=y;
nxt[l]=head[x];
head[x]=l;
}
void insert1(int x,int y)
{
l1++;
ver1[l1]=y;
nxt1[l1]=head1[x];
head1[x]=l1;
}
void Tarjan(int x)
{
bool tag=0;
dfn[x]=low[x]=++tim;
s[++top]=x;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(!scc[y]) low[x]=min(low[x],dfn[y]);
if(y==x) tag=1;
}
if(dfn[x]==low[x]){
cnt++;
while(1){
int y=s[top--];
scc[y]=cnt;
sum[cnt]++;
if(x==y) break;
}
if(tag) sum[cnt]=-1;
}
}
void dfs(int x)
{
vis[x]=1;
tmp++;
for(int i=head1[x];i;i=nxt1[i]){
if(!vis[ver1[i]]) dfs(ver1[i]);
}
}
signed main()
{
n=read();
for(i=1;i<=n;i++){
int u=read(),v=read();
insert(u,v);
pos[u]=pos[v]=1;
}
for(i=1;i<=2*n;i++){
if(!dfn[i]&&pos[i]) Tarjan(i);
}
for(i=1;i<=2*n;i++){
for(j=head[i];j;j=nxt[j]){
if(scc[i]!=scc[ver[j]]){
insert1(scc[ver[j]],scc[i]);
d[scc[i]]++;
}
}
}
int ans=1;
for(i=1;i<=cnt;i++){
if(d[i]==0){
if(sum[i]==1){
tmp=0;
dfs(i);
ans=ans*tmp%mod;
}
else if(sum[i]>0) ans=ans*2%mod;
}
}
cout<<ans<<endl;
return 0;
}