【题意】
有 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; }