1. 程式人生 > >bzoj2560串珠子(子集dp)

bzoj2560串珠子(子集dp)

答案 times bzoj size namespace 自己 for roman sin

銘銘有n個十分漂亮的珠子和若幹根顏色不同的繩子。現在銘銘想用繩子把所有的珠子連接成一個整體。
  現在已知所有珠子互不相同,用整數1到n編號。對於第i個珠子和第j個珠子,可以選擇不用繩子連接,或者在ci,j根不同顏色的繩子中選擇一根將它們連接。如果把珠子看作點,把繩子看作邊,將所有珠子連成一個整體即為所有點構成一個連通圖。特別地,珠子不能和自己連接。
  銘銘希望知道總共有多少種不同的方案將所有珠子連成一個整體。由於答案可能很大,因此只需輸出答案對1000000007取模的結果。

Solution

神仙dp。

我們先令g[i]表示在i這個狀態中,隨意連邊的方案數,這個可以輕松的搞出來。

然後我們再考慮從狀態中減去不合法的,我們可以考慮枚舉子集,把當前集合強行分成不連通的兩個集合,這樣的方案數就是f[s]*g[s^i].

為了避免算重復,我們需要從集合中找出一個固定點,強制讓這個點在S集合中,這樣就不會出現我們在g[s^i]中算了一遍後又在g[s]算了一遍。

Code

#include<iostream>
#include<cstdio>
#define N 22
using namespace std;
const int mod=1000000007;
long long a[N][N],f[1<<17],g[1<<17];
int n;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
      
for(int j=1;j<=n;++j)scanf("%lld",&a[i][j]); int ma=(1<<n)-1; for(int i=1;i<=ma;++i){ g[i]=1; for(int j=1;j<=n;++j)if(i&(1<<j-1)) for(int k=j+1;k<=n;++k)if(i&(1<<k-1)) (g[i]*=(a[j][k]+1))%=mod; } for(int
i=1;i<=ma;++i){ for(int S=i&(i-1);S;S=i&(S-1)) if(!((S^i)&(i&-i)))(f[i]+=(f[S]*g[S^i])%mod)%=mod; f[i]=((g[i]-f[i])%mod+mod)%mod; } printf("%lld",f[ma]); return 0; }

 

bzoj2560串珠子(子集dp)