1. 程式人生 > >【題解】bzoj2560串珠子

【題解】bzoj2560串珠子

include pac getchar() mod IT 沒有 esp define name

  挺強的……容斥+狀壓DP。首先想到如果可以求出f[k],f[k]代表聯通狀態為k的情況下的合法方案數,則f[k] = g[k] - 非法方案數。g[k]為總的方案數,這是容易求得的。那麽非法方案數我們可以枚舉 k 的子集 j,則 j 聯通而剩下的則隨意連(不與j聯通)。可是做到這裏以為自己做出來的,實際上並沒有……

  註意到枚舉到子集 j 時,若 s‘ = k - j, 那如果 s‘ 中有一個聯通的方案 s‘‘,我們在這裏減去一次,在之後枚舉到s‘‘時又會枚舉到這個方案一次。實際上,這也就是說0111與1000這兩個子集是對稱的。所以我們為了避免這樣的情況,就鎖定一個點a,使得點 a 一定不出現在集合 j 中,可以使得 a 只能出現在集合 s‘ 中,也就避免了重復。

  代碼有參考,如有雷同,是我抄的 (o′ω`o)?

#include <bits/stdc++.h>
using namespace std;
#define maxn 20
#define maxm ((1 << 16) + 2)
#define mod 1000000007
#define int long long
int n, bin[maxn], a[maxn][maxn];
int f[maxm], g[maxm];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < 
0 || c > 9) { if(c == -) k = -1; c = getchar(); } while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * k; } signed main() { n = read(); bin[0] = 1; for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) a[i][j] = read();
for(int i = 1; i <= n; i ++) bin[i] = bin[i - 1] << 1; for(int k = 0; k < bin[n]; ++ k) { f[k] = 1; for(int i = 1; i < n; i ++) if(k & bin[i - 1]) for(int j = i + 1; j <= n; j ++) if(k & bin[j - 1]) f[k] = f[k] * (a[i][j] + 1) % mod; g[k] = f[k]; int K = (k ^ (k & -k)); for(int j = K; j; j = (j - 1) & K) f[k] = (f[k] - g[j] * f[k ^ j] % mod + mod) % mod; } printf("%lld\n", f[bin[n] - 1]); return 0; }

【題解】bzoj2560串珠子