P5933 [清华集训2012] 串珠子
P5933 [清华集训2012] 串珠子 题解
Link
非常好的一道状压题目(为啥自己总是想不到呢……)。
首先我们发现
没错,内部点连边的方案数很显然,我们只需要算出内部点不连通的方案,然后减走就行了。
其实,我们发现,内部连通的方案数之所以难算,就是因为只需要一条边就可以连通,方案之间的界限是模糊的;而如果不连通,我们只需要规定哪两部分不连通就行了。
因此,我们有了这样一个方案。我们现在要计算
关于具体实现,首先要钦定一个点
代码(蛮乱的):
#include<bits/stdc++.h> using namespace std; const int mod = 1000000007; const int N = 20; const int NN = 70000; inline int read(){ int x = 0; char ch = getchar(); while(ch<'0' || ch>'9') ch = getchar(); while(ch>='0'&&ch<='9') x = x*10+ch-48, ch = getchar(); return x; } inline int fpow(int a, int b){ int ret = 1; a%=mod; while(b){ if(b & 1){ ret = (1ll*ret*a)%mod; } a = (1ll*a*a)%mod; b>>=1; } return ret; } int c[N][N]; int n; int f[NN]; int dp[NN]; int main(){ n = read(); for(int i = 1; i<=n; ++i){ for(int j = 1; j<=n; ++j){ c[i][j] = read(); } } for(int s = 1; s<=(1<<n)-1; ++s){ f[s] = 1; for(int i = 1; i<=n; ++i){ if((s>>(i-1))&1){ for(int j = i+1; j<=n; ++j){ if((s>>(j-1))&1){ f[s] = (1ll*(c[i][j]+1)*f[s])%mod; } } } } } for(int s = 1; s<=(1<<n)-1; ++s){ int tmp; // for(int i = 1; i<=n; ++i){ // if((s>>(i-1))&1){ // tmp = (1<<(i-1)); // break; // } // } for(int i = n; i>=1; --i){ if((s>>(i-1))&1){ tmp = (1<<(i-1)); break; } } int ts = s^tmp; int Cs = 0; for(int subs = ts; subs; subs = ts&(subs-1)){ Cs = (1ll*Cs+1ll*f[subs]*dp[s^subs]%mod)%mod; } // cout << Cs << " "; dp[s] = ((f[s]-Cs)%mod+mod)%mod; // Cs = 0; // for(int subs = ts; subs; subs = ts&(subs-1)){ // Cs = (1ll*Cs+1ll*f[s^subs]*dp[subs]%mod)%mod; // } // cout << Cs << endl; } printf("%d\n", dp[(1<<n)-1]); return 0; }