题目意思:
给你一个有向图,顶点从1到n编号。
边的形式是:
1->x1
2->x2
...
n->xn
x1,x2..xn是1到n
问,若可以改变边的方向,那么有多少种有向图满足途中没有循环路径?
显然,答案 = 2*(连通环1边数-2)* 2*(连通环2边数-2).... *2^ (不构成连通环的边数)
接下来,就是bfs判环
在本题中,图中存在环的条件是由一起点搜索下去可构一回路,因此,只要设置每个点的原始起点即可(类似于并查集)
#include <bits/stdc++.h>
#define MOD 1e9+7
using namespace std;
const int N = 200050;
typedef long long ll;
int father[N],cnt[N];///标记结点是否被访问过
int n,p,q,a[N];///p是构成环的边数,q是不构成环的边数
ll ans;
ll q_pow(ll a,ll b,ll c)
{
ll ans = 1;
while(b){
if(b&1) ans = ans * a %c;
a = a * a % c;
b >>= 1;
}
return ans;
}
void dfs(int depth,int now,int fa)
{
father[now] = fa;
cnt[now] = depth;
if(!father[a[now]]) dfs(depth+1,a[now],fa);
else if(father[a[now]] == fa){
p += cnt[now] - cnt[a[now]] + 1;
ans = ans * (q_pow(2,(cnt[now] - cnt[a[now]] + 1),MOD) - 2);
}
}
int main()
{
ans = 1;
memset(cnt,0,sizeof(cnt));
memset(father,0,sizeof(father));
scanf("%d",&n);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
p = q = 0;
for(int i = 1;i <= n;++ i){
if(father[i]) continue;
else
dfs(0,i,i);
}
q = n - p;
ans = ans * q_pow(2,q,MOD);
printf("%lld\n",ans);
}
因为标记了每个点,因此,时间复杂度就是O(n)