链接
传送门
题意
给出\(n\)个点,每个点都有一条弧连接着一个其他的点,对于给出的弧,可以选一部分进行反转使得图中没有环,输出操作的方法数对\(10^9+7\)取模后的值。
思路
易得,弧的方向对结果不产生影响,将弧转为无向边考虑。显然,对于任意连通分量都至多有一个环。每个环的反转方式为\(2^e-2\),不在环中的每条边的反转状态任意。最终的结果为\(2^{n-\sum e} \prod {\left(2^e-2\right)}\)。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue>
using namespace std;
typedef long long LL;
const LL mod = 1000000007; const int maxn = 200005;
int p[maxn], a[maxn], d[maxn]; bool vis[maxn]; vector<int> g[maxn]; queue<int> q;
int Find(int x) { return x == p[x] ? x : p[x] = Find(p[x]); }
LL fexp(LL x, LL k) { LL res = 1, cur = x; while (k) { if (k & 1) { res = res * cur % mod; } cur = cur * cur % mod; k >>= 1; } return res; }
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { p[i] = i; scanf("%d", &a[i]); } int m = 0; LL ans = 1; for (int i = 1; i <= n; ++i) { int fu = Find(i), fv = Find(a[i]); if (fu != fv) { p[fu] = fv; g[i].push_back(a[i]); g[a[i]].push_back(i); } else { q.push(i); vis[i] = true; d[i] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int v: g[u]) { if (!vis[v]) { vis[v] = true; d[v] = d[u] + 1; q.push(v); } } } m += d[a[i]]; ans = ans * (fexp(2, d[a[i]]) - 2) % mod; } } printf("%d\n", int(ans * fexp(2, n - m) % mod)); return 0; }
|