【GDOI2020模擬4.11】贏家(winner) (計數dp+容斥)
阿新 • • 發佈:2020-04-12
題目描述:
PinkRabbit 是一位人贏。
福州市可以抽象成一個n個點m條邊的,不包含重邊與自環的無向圖,PinkRabbit 住在1號
點,而他的妹子住在2號點。
某一天,PinkKitten 施放了一個大魔法,讓這個無向圖上所有的邊都變成了單向邊。現在
PinkRabbit 關心的是他是否能夠和他的妹子見面。
具體地,PinkRabbit 能和他的妹子見面,當且僅當存在一個點 u,滿足新圖上從1號點出發能夠
到達u,從2號點出發也能到達 。
現在你需要計算出,在把所有 m條邊進行定向的所有2^m 種方案中,有多少種方案能讓
PinkRabbit 和他的妹子見面。你只需輸出其對10^9+7 取模後的結果。
\(n\le 15\)
https://gmoj.net/senior/#main/show/6554
失智了,居然寫了個完完全全的dp,還過了樣例,然後WA0.
考慮用總方案-不合法方案。
對於不合法方案,列舉1能走到的點集是\(S\),2能走到的點集是\(T\),\(S∩T=?\)。
設\(Z=總集-S-T\)
那麼\(Z\)和\(S、T\)之間的邊方向確定,\(S\)和\(T\)之間不能有邊。
現在就是求\(S\)的方案數(\(T\)同理)。
設\(f[S]\)表示\(S\)的方案數,同樣用總-不合法。
不合法就是是\(S\)的一個子集\(S‘\),\(f[S]-=f[S‘]*(S‘和S-S‘中間的邊定向)\)
預處理\(cnt[S]\)
Code:
#include<bits/stdc++.h> #define fo(i,x,y) for(int i = x,_b = y; i <= _b; i ++) #define ff(i,_b = y; i < _b; i ++) #define fd(i,_b = y; i >= _b; i --) #define ll long long #define pp printf #define hh pp("\n") using namespace std; const int mo = 1e9 + 7; const int N = 16; int n,m,id,y; int b[N][N]; int cnt[1 << 15]; ll a2[N * N]; ll f[1 << 15],g[1 << 15]; int main() { freopen("winner.in","r",stdin); freopen("winner.out","w",stdout); scanf("%d %d %d",&n,&m,&id); fo(i,1,m) scanf("%d %d",&x,&y),b[x][y] = b[y][x] = 1; a2[0] = 1; fo(i,n * n) a2[i] = a2[i - 1] * 2 % mo; ff(s,1 << n) { fo(i,n - 1) if(s >> i & 1) { cnt[s] = cnt[s ^ (1 << i)]; fo(j,n - 1) if(s >> j & 1) cnt[s] += b[i + 1][j + 1]; break; } } ff(s,1 << n) { if(s & 1) { f[s] = a2[cnt[s]]; for(int t = (s - 1) & s; t > 0; t = (t - 1) & s) f[s] = (f[s] - f[t] * a2[cnt[s ^ t]]) % mo; } if(s & 2) { g[s] = a2[cnt[s]]; for(int t = (s - 1) & s; t > 0; t = (t - 1) & s) g[s] = (g[s] - g[t] * a2[cnt[s ^ t]]) % mo; } } ll ans = a2[m]; ff(s,1 << n) if(f[s]) { for(int t = s + 1; t < (1 << n); t = (t + 1) | s) if(g[s ^ t]) { int z = (1 << n) - 1 - t; if(cnt[s ^ z] + cnt[s ^ t ^ z] - cnt[z] == m) { ans = (ans - f[s] * g[s ^ t] % mo * a2[cnt[z]]) % mo; } } } ans = (ans % mo + mo) % mo; pp("%lld\n",ans); }