题目大意:给你n个点n条边的有向图,你可以任意地反转一条边的方向,也可以一条都不反转,问你有多少种反转的方法
使图中没有环。
思路:我们先把有向边全部变成无向边,每个连通图中肯定有且只有一个环,如果这个连通图里边有n个点,环由m个元素
构成,那么这个连通图的反转方法数为,(2^(n-m)) * (2^m-2),然后将所有连通图的种数乘到一起就好啦。具体求圆环由几
个点组成看代码。
ps:最后算ans的时候忘了加括号,debug了一个小时QAQ。
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=2*1e5+5; const ll mod=1e9+7; vector<ll> e[N]; ll n,pre[N],S,E,cnt,dfn[N],idext,ans; ll q_pow(ll a,ll b) { ll ans=1; while(b) { a%=mod; if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans; } void dfs(ll v,ll p) { pre[v]=p; cnt++; dfn[v]=++idext; for(ll i:e[v]) { if(i==p) continue; if(pre[i] && S==-1) { if(dfn[i]<dfn[v]) E=v,S=i; else E=i,S=v; } if(pre[i]==0) dfs(i,v); } } int main() { scanf("%lld",&n); for(ll i=1;i<=n;i++) { ll g; scanf("%lld",&g); e[g].push_back(i); e[i].push_back(g); } ans=1; for(ll i=1;i<=n;i++) { cnt=0; S=E=-1; if(pre[i]==0) { dfs(i,-1); ll num=1; while(E!=S) { num++; E=pre[E]; } ll res=ans; ans=ans*((q_pow(2,cnt-num)*(q_pow(2,num)-2))%mod)%mod; } } printf("%lld ",ans); return 0; }