P4547 [THUWC2017]隨機二分圖(狀壓,期望DP)
阿新 • • 發佈:2020-07-20
期望好題。
發現 \(n\) 非常小,應該要想到狀壓的。
我們可以先只考慮 0 操作。
最難的還是狀態:
我們用 \(S\) 表示左部點有哪些點已經有對應點, \(T\) 表示右部點有哪些點已經有對應點,\(f[S][T]\) 表示從一條邊沒連到此狀態的期望方案數
這樣就有轉移:
\[f[S][T] <- \sum_{s \in S,t \in T}f[S \oplus s][T \oplus t] * p(s, t) \]
也就是說,從沒選的點中選倆點連邊。不過這可能會算重(先連 \(e_1\) 後連 \(e_2\) 和先連 \(e_2\) 後連 \(e_1\) 都會計入答案)。因此我們強制要求選擇 \(S\) 中編號最小的點連邊,這樣每種答案就只被計算一次了。
然後考慮 1,2 操作。1,2 操作並沒有改變兩條邊各自出現的概率,只是改變的兩條邊同時出現的概率。因此我們可以加一些特殊的邊 \(e_1, e_2\) ,並要求 \(e_1, e_2\) 必須一起被選,這樣就可以調節同時出現的概率。具體來說就是 1 操作加 \(\frac{1}{4}\),2操作減 \(\frac{1}{4}\)。
然後就沒啥難點了。考慮到合法狀態數不多,直接記憶化搜尋即可。
const int P = 1e9 + 7; inline ll quickpow(ll x, int k) { ll res = 1; while (k) { if (k & 1) res = res * x % P; x = x * x % P; k >>= 1; } return res; } int inv2, inv4; int n, m; struct edge { int s, t, p; }e[N]; int ecnt; map<int, ll> mp[NN]; inline ll dfs(int S, int T) { if (S == 0 && T == 0) return 1; if (mp[S].count(T)) return mp[S][T]; ll res = 0; for (register int i = 1; i <= ecnt; ++i) { int s = e[i].s, t = e[i].t, p = e[i].p; if (((S | s) != S) || ((T | t) != T)) continue; if ((s | (lowbit(S))) != s) continue; res = (res + dfs(S ^ s, T ^ t) * p) % P; } mp[S][T] = res; return res; } int main() { inv2 = quickpow(2, P - 2); inv4 = quickpow(4, P - 2); read(n), read(m); for (register int i = 1; i <= m; ++i) { int t, x, y; read(t), read(x), read(y); --x, --y; e[++ecnt] = (edge){1 << x, 1 << y, inv2}; if (t) { int a, b; read(a), read(b); --a, --b; e[++ecnt] = (edge){(1 << a), (1 << b), inv2};//Attention! if (a == x || b == y) continue; e[++ecnt] = (edge){(1 << x) | (1 << a), (1 << y) | (1 << b), t == 1 ? inv4 : P - inv4}; } } ll ans = dfs((1 << n) - 1, (1 << n) - 1) * (1 << n) % P; printf("%lld\n", ans); return 0; }