题目链接:http://codeforces.com/problemset/problem/711/D
给你一个n个节点n条边的有向图,可以把一条边反向,现在问有多少种方式可以使这个图没有环。
每个连通量必然有一个环,dfs的时候算出连通量中点的个数y,算出连通量的环中点的个数x,所以这个连通量不成环的答案是2^(y - x) * (2^x - 2)。
最后每个连通量的答案相乘即可。
1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <ctime> 10 #include <list> 11 #include <set> 12 #include <map> 13 using namespace std; 14 typedef __int64 LL; 15 typedef pair <int, int> P; 16 const int N = 2e5 + 5; 17 struct Edge { 18 int next, to; 19 }edge[N << 1]; 20 LL mod = 1e9 + 7, d[N], ans, f[N], sum; 21 int head[N], cnt; 22 bool vis[N]; 23 24 LL fpow(LL a, LL b) { 25 LL res = 1; 26 while(b) { 27 if(b & 1) 28 res = res * a % mod; 29 a = a * a % mod; 30 b >>= 1; 31 } 32 return res; 33 } 34 35 inline void add(int u, int v) { 36 edge[cnt].next = head[u]; 37 edge[cnt].to = v; 38 head[u] = cnt++; 39 } 40 41 void dfs(int u, int p, int dep) { 42 if(!ans && vis[u]) { 43 ans = dep - d[u] ? dep - d[u] : 2; 44 return ; 45 } else if(vis[u]) { 46 return ; 47 } 48 d[u] = dep; 49 vis[u] = true; 50 ++sum; 51 for(int i = head[u]; ~i; i = edge[i].next) { 52 int v = edge[i].to; 53 if(v == p) 54 continue; 55 dfs(v, u, dep + 1); 56 } 57 } 58 59 int main() 60 { 61 f[0] = 1; 62 for(int i = 1; i < N; ++i) { 63 f[i] = f[i - 1]*2LL % mod; //2的阶乘预处理 64 } 65 memset(head, -1, sizeof(head)); 66 cnt = 0; 67 int n, u; 68 scanf("%d", &n); 69 for(int i = 1; i <= n; ++i) { 70 scanf("%d", &u); 71 add(u, i); 72 add(i, u); 73 } 74 LL res = 1; 75 for(int i = 1; i <= n; ++i) { 76 if(!vis[i]) { 77 ans = sum = 0; 78 dfs(i, -1, 1); 79 res = ((res * (f[ans] - 2) % mod + mod) % mod * f[sum - ans]) % mod; 80 } 81 } 82 printf("%lld\n", res); 83 return 0; 84 }
时间: 2024-11-06 02:51:46