【BZOJ2560】串珠子 狀壓DP+容斥
阿新 • • 發佈:2017-11-19
輸入 漂亮 整數 clu 狀壓dp 無向連通圖 ret div 枚舉
現在已知所有珠子互不相同,用整數1到n編號。對於第i個珠子和第j個珠子,可以選擇不用繩子連接,或者在ci,j根不同顏色的繩子中選擇一根將它們連接。如果把珠子看作點,把繩子看作邊,將所有珠子連成一個整體即為所有點構成一個連通圖。特別地,珠子不能和自己連接。
銘銘希望知道總共有多少種不同的方案將所有珠子連成一個整體。由於答案可能很大,因此只需輸出答案對1000000007取模的結果。
0 2 3
2 0 4
3 4 0
【BZOJ2560】串珠子
Description
銘銘有n個十分漂亮的珠子和若幹根顏色不同的繩子。現在銘銘想用繩子把所有的珠子連接成一個整體。現在已知所有珠子互不相同,用整數1到n編號。對於第i個珠子和第j個珠子,可以選擇不用繩子連接,或者在ci,j根不同顏色的繩子中選擇一根將它們連接。如果把珠子看作點,把繩子看作邊,將所有珠子連成一個整體即為所有點構成一個連通圖。特別地,珠子不能和自己連接。
銘銘希望知道總共有多少種不同的方案將所有珠子連成一個整體。由於答案可能很大,因此只需輸出答案對1000000007取模的結果。
Input
標準輸入。輸入第一行包含一個正整數n,表示珠子的個數。接下來n行,每行包含n個非負整數,用空格隔開。這n行中,第i行第j個數為ci,j。
Output
標準輸出。輸出一行一個整數,為連接方案數對1000000007取模的結果。
Sample Input
30 2 3
2 0 4
3 4 0
Sample Output
50HINT
對於100%的數據,n為正整數,所有的ci,j為非負整數且不超過1000000007。保證ci,j=cj,i。每組數據的n值如下表所示。
編號 1 2 3 4 5 6 7 8 9 10
n 8 9 9 10 11 12 13 14 15 16
題解:還記得n個點有標號的無向連通圖個數怎麽求嗎?如果記得的話,此題就簡單了。
用f[S]表示與1號點連通的點的狀態為S的方案數。我們先與處理出g數組,$g[S]=\prod\limits_{u,v \in S} (c[u][v]+1)$,然後f[S]就等於g[S]減去S中某些點與1號點不連通的方案數。那麽我們枚舉此時與1號點連通的點的狀態,其余的點與這個連通塊均沒有邊相連,但是其余的點之間可以任意連邊,所以有:
$f[S]=\sum\limits_{S‘ \subsetneq S}f[S‘]\times g[S - S‘ ]$
所以時間復雜度就是枚舉子集的$O(n^3)$。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll P=1000000007; int n,m; int c[20][20]; int v[20],p[20],ref[1<<16],Log[1<<16]; ll f[1<<16],g[1<<16]; int main() { scanf("%d",&n); int i,j,u; for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&c[i][j]); for(i=0;i<n;i++) Log[1<<i]=i; g[0]=1; for(i=1;i<(1<<n);i++) { ll tmp=1; for(u=Log[i&-i],j=i-(i&-i);j;j-=j&-j) tmp=tmp*(c[u][Log[j&-j]]+1)%P; g[i]=g[i-(i&-i)]*tmp%P; } for(i=1;i<(1<<n);i++) { m=0; for(j=i-(i&-i);j;j-=j&-j) p[m++]=j&-j; for(j=1;j<(1<<m);j++) { ref[j]=ref[j-(j&-j)]|p[Log[j&-j]]; f[i]=(f[i]+f[i^ref[j]]*g[ref[j]])%P; } f[i]=(g[i]-f[i]+P)%P; } printf("%lld",f[(1<<n)-1]); return 0; }
【BZOJ2560】串珠子 狀壓DP+容斥