Codeforces Round #369 (Div. 2)-D Directed Roads

题目大意:给你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;
}
View Code
原文地址:https://www.cnblogs.com/CJLHY/p/7891912.html