题意:
铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在c[i,j]根不同颜色的绳子中选择一根将它们连接。
如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。
n<=16,a[i][j]<=1e9+7
思路:
还记得n个点有标号的无向连通图个数怎么求吗?如果记得的话,此题就简单了。
用f[S]表示与1号点连通的点的状态为S的方案数。我们先与处理出g数组,g[S]=∏u,v∈S(c[u][v]+1),然后f[S]就等于g[S]减去S中某些点与1号点不连通的方案数。
那么我们枚举此时与1号点连通的点的状态,其余的点与这个连通块均没有边相连,但是其余的点之间可以任意连边,
所以有:
f[S]=∑S′⊊Sf[S′]×g[S−S′]
所以时间复杂度就是枚举子集的O(3^n)
需要注意的是k=log(x)/log(2)+1这种写法似乎在BZOJ上不能用,需要预处理或者暴力
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstring> 6 #include<map> 7 #include<set> 8 #include<cmath> 9 using namespace std; 10 long long dp[1<<17],f[1<<17]; 11 long long a[17][17]; 12 const int MOD=1000000007; 13 int n; 14 15 int lowbit(int x) 16 { 17 return (x&(-x)); 18 } 19 20 int who(int x) 21 { 22 int s=0; 23 int k=x; 24 while(k) 25 { 26 s++; 27 k>>=1; 28 } 29 return s; 30 } 31 32 int main() 33 { 34 35 scanf("%d",&n); 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=n;j++) scanf("%lld",&a[i][j]); 38 f[0]=1; 39 40 int M=(1<<n)-1; 41 for(int i=1;i<=M;i++) 42 { 43 int x=lowbit(i); 44 int y=who(x); 45 f[i]=f[i-x]; 46 for(int j=1;j<=n;j++) 47 if((y!=j)&&(i&(1<<(j-1)))) f[i]=f[i]*(a[y][j]+1)%MOD; 48 } 49 50 dp[1]=1; 51 for(int i=2;i<=M;i++) 52 if(i&1) 53 { 54 int v=(i-1); 55 dp[i]=f[i]; 56 while(v) 57 { 58 dp[i]=dp[i]-(f[v]*dp[i-v])%MOD; 59 dp[i]=(dp[i]%MOD+MOD)%MOD; 60 v=((i-1)&(v-1)); 61 } 62 } 63 printf("%lld ",dp[M]); 64 return 0; 65 }