UOJ #37. 【清華集訓2014】主旋律(容斥+dp)
阿新 • • 發佈:2022-05-23
題目大意
給出一個 \(n\) 個點的有向圖,無重邊無自環,求其強連通的生成子圖個數。\(n\leq 15\)。
前置知識:有向圖的 DAG 生成子圖計數
題目大意
給出一個 \(n\) 個點的有向圖,無重邊無自環,求其無環的生成子圖個數。\(n\leq 17\)。
題意分析
注意到對於任意一個 DAG,總有一些入度為零的點。不妨欽定一些點入度為零,然後進行容斥計數。設 \(dp_S\) 表示 \(S\) 這個點集的 DAG 生成子圖個數。考慮列舉 \(S\) 的一個非空子集 \(T\),記其在 \(S\) 中的補集為 \(T'\),欽定 \(T\) 中的點入度為零。對於所有由 \(T\) 中某點指向 \(T'\)
時間複雜度
暴力做複雜度為 \(O(n\times 3^n)\),加一些簡單的小優化可以做到 \(O(3^n)\),具體看程式碼實現。
程式碼
#include <bits/stdc++.h> using namespace std; constexpr int MAXN = 17, MAXM = 273, MOD = 1e9 + 7; int n, m, dp[1 << MAXN], out[MAXN][1 << MAXN], in[MAXN][1 << MAXN], pw2[MAXM], ppcnt[1 << MAXN], val[1 << MAXN], low[1 << MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); pw2[0] = 1; for (int i = 1; i < MAXM; ++i) pw2[i] = pw2[i - 1] * 2 % MOD; cin >> n >> m; while (m--) { int u, v; cin >> u >> v, --u, --v; ++out[u][1 << v], ++in[v][1 << u]; } for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int S = 0; S < 1 << n; ++S) { if (S >> j & 1) { out[i][S] += out[i][S ^ 1 << j]; in[i][S] += in[i][S ^ 1 << j]; } } } } for (int S = 1; S < 1 << n; ++S) ppcnt[S] = ppcnt[S >> 1] + (S & 1); for (int S = 1; S < 1 << n; ++S) if (!(S & 1)) low[S] = low[S >> 1] + 1; dp[0] = 1; for (int S = 1; S < 1 << n; ++S) { for (int T = S; T; T = (T - 1) & S) { if (T == S) { val[T] = 0; } else { int p = low[S ^ T], nT = T | 1 << p; val[T] = val[nT] - out[p][S ^ nT] + in[p][T]; } dp[S] = (dp[S] + (ppcnt[T] & 1 ? 1ll : -1ll) * pw2[val[T]] * dp[S ^ T]) % MOD; } } cout << (dp[(1 << n) - 1] + MOD) % MOD << "\n"; return 0; }