题意:给定n个点n条边(n<=2e5)的有向图,每个顶点都只有一条出边,给定op:将若干条边翻转,问有多少种op使得,操作后的图不存在环
关键:因为每个点的出边只有一个,若有u->v路径无环,则u->v为简单的路径只有n-1条边,则该路径边可以随意反转 (v的出边可能和其他边形成环)
在环上的边至少反转一条即可变为无环(环变为若干条链),但不能全部翻转.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=2e5+20;
int to[N];//每个点出度为1
int T,n,id[N],tim[N];
vector<int> num;//每个环上的顶点数
ll d[N];//2^i mod 1e9
void dfs(int x,int t)
{
id[x]=t;//dfs顺序
tim[x]=T;//第T个连通分量
int v=to[x];
if(id[v]!=-1&&tim[v]==T)//找到环
{
num.push_back(id[x]-id[v]+1);//环上的点
}
else if(id[v]==-1)
{
dfs(v,t+1);
}
}
int main()
{
d[0]=1;
for(int i=1;i<N;i++)
d[i]=(d[i-1]*2ll)%mod;
while(cin>>n)
{
memset(id,-1,sizeof(id));
T=0;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
to[i]=x;
}
for(int i=1;i<=n;i++)//遍历每个连通分量
{
if(id[i]==-1)//
{
++T;
dfs(i,1);
}
}
ll ans=1,cnt=0;//
for(int i=0;i<num.size();i++)
{
//环上边不能全翻转和都不翻转
ans=ans*(d[num[i]]-2+mod)%mod;
cnt+=num[i];
}
cnt=n-cnt;
ans=(ans*d[cnt])%mod;//不在环上的边
cout<<ans<<endl;
num.clear();
}
return 0;
}