分析:
给出n个点,每个点的出度为1,然后给出每个点指向的那个点。求有多少种翻转的方法(选中哪些边进行一次反向)使得翻转后整个有向图中不存在环。范围最大为200000。题解:
通过分析可得,整个图中有多少点就有多少边。我们只需要求出整个有向图中有多少个环,以及每一个环里有多少条边,然后就是一个组合问题:不在环里的边(假设有x条)的选择方法有: C(0x)+...C(xx)=2x
每一个环里的边(假设有t条)的选择方法有: C(1t)+...C(t−1t)=2t−2
使用快速幂即可计算。
- AC代码:
/*************************************************************************
> File Name: test.cpp
> Author: Akira
> Mail: qaq.febr2.qaq@gmail.com
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;
#define MaxN 212345
#define MaxM MaxN*10
#define INF 0x3f3f3f3f
#define bug cout<<88888888<<endl;
const int mod = 1000000007;
int n;
int A[MaxN];
map<int, int> M;
int huan[MaxN];
void init()
{
CLR(huan);
M.clear();
}
LL multi(LL a, LL b)
{
LL ret = 1;
while(b>0)
{
if(b&1)
ret=(ret*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ret;
}
int vis[MaxN];
void Find(int x,int num)
{
vis[x] = num; //记录从初始点出发走到该点要多少条边
int y = A[x]; //找到x指向的点y
if(M[y] && M[y]!=M[x]) return ; //如果y点被访问过并且是在以前查找中被访问过则退出
if(M[y] == M[x]) //如果y点被访问过并且是在当前查找中被访问过则记录为环
{
huan[++huan[0]] = num+1 - vis[y];
return;
}
else //如果y点没有被访问过,则继续访问
{
M[y] = M[x];
Find(y, num+1); //走过的边数+1。
}
}
int main()
{
while(~scanf("%d", &n))
{
init();
for(int i=1;i<=n;i++)
{
scanf("%d", &A[i]);
}
for(int i=1;i<=n;i++)
{
if(M[i]) continue;
M[i] = i;
Find(i,0);
}
//计算答案
LL ans = 0;
LL cnt = 0;
LL b = 1;
for(int i=1;i<=huan[0];i++)
{
cnt += huan[i];
b *= (( multi(2, huan[i]) - 2) % mod);
b%=mod;
}
LL a = multi(2, n - cnt);
ans = a * b % mod;
cout << ans << endl;
}
//system("pause");
}