CF711D

题意

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

题解

\(n\) 个点和 \(n\) 条边。

所以是基环树

先只考虑单棵基环树的情况。

使得图中不出现环。

基环树环外的边对题目没有影响,既不会影响当前的环,也不会增加新的环。

如果基环树的环上的任意一条边与其他的边的方向不同,就不会产生环。

所以在所有的方案中,只有环上边的方向全部相同的情况是会产生环的。

假设环上边的条数为 \(m\),那么此时的产生环的方案数就是 \(2\times2^{n-m}\) 种,不产生环的方案数就是 \((2^m+2)\times2^{n-m}\) 种。

因为题目不保证图联通,所以考虑基环树森林的情况。

所以对于基环树森林中的每一棵基环树,找出其中的环,都像上面那样计算,将结果相加就是答案。

所以最终的答案是: $$2^{n- \sum m} \coprod{(2^m-2)}$$

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5;int n,m=1e9+7,tot,vis[N],si[N],a[N],b[N],ans=1;
struct edge{int v,nt;}e[N<<1];
int fir[N],cnt;
void add(int u,int v){e[++cnt].v=v;e[cnt].nt=fir[u];fir[u]=cnt;}
int k(int a,int b){int s=1;while(b){if(b&1)(s*=a)%=m;(a*=a)%=m;b>>=1;}return s;}
void dfs(int u,int c){
	if(vis[u]){b[tot]=max(b[tot],c-si[u]);return;}
	si[u]=c;vis[u]=1;a[tot]++;
	for(int i=fir[u];i;i=e[i].nt)dfs(e[i].v,c+1);
}signed main(){
	std::ios::sync_with_stdio(false);
	cin>>n;for(int i=1,x;i<=n;i++)	cin>>x,add(i,x),add(x,i);
	for(int i=1;i<=n;i++)
		if(!vis[i])	tot++,dfs(i,0);
	for(int i=1;i<=tot;i++)
		ans=(ans*((k(2,a[i])-k(2,a[i]-b[i]+1))%m)%m+m)%m;
	cout<<ans<<endl;return 0;
}
posted @ 2022-02-18 21:36  AIskeleton  阅读(23)  评论(0编辑  收藏  举报