CF711D Directed Roads

【题意】

有 n 个点和 n 条边,第 i 条边从 i 连到 ai 。 每条边需要指定一个方向(无向边变为有向边)。问有多少种指定方向 的方案使得图中不出现环

【分析】

题目给定的是一个基环树森林,要求我们把无向边定向,问不包含环的方案数

显然这个环只能来自基环树的环

考虑一棵基环树,只要环上有一条边不是按照相同的方向(顺时针/逆时针)的话,那么就不存在环

所以一棵环上有cnt条边的基环树的方案数为$2^n-2^{n-len+1}$这里+1是因为有顺时针/逆时针两种情况

那么对于整个森林答案为$2^{n-\sum{len}}\prod(2^{len}-2)$

 

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int mod=1e9+7;
int n,head[maxn],tot;
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++tot].to=y; e[tot].nxt=head[x]; head[x]=tot;
}
int cir[maxn],top,dep[maxn],vis[maxn];
void dfs(int u,int d)
{
    dep[u]=d; vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(!vis[to]) dfs(to,d+1);
        else if(vis[to]==1) cir[++top]=dep[u]-dep[to]+1;
    }
    vis[u]=-1;
}
int pw[maxn];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x);
    }
    for(int i=1;i<=n;i++) if(!dep[i]) dfs(i,0);
    pw[0]=1;
    for(int i=1;i<=n;i++) pw[i]=1ll*pw[i-1]*2%mod;
    int sum=0,PI=1;
    for(int i=1;i<=top;i++)
    {
        sum=(sum+cir[i])%mod;
        PI=1ll*PI*(pw[cir[i]]-2)%mod;
    }
    int ans=1ll*pw[n-sum]*PI%mod;
    // printf("%d %d\n",sum,PI);
    printf("%d",(ans+mod)%mod);
    return 0;
}

 

posted @ 2021-07-04 10:23  andyc_03  阅读(52)  评论(0编辑  收藏  举报