【BZOJ2560】串珠子(状压DP,容斥原理)

题意:

铭铭有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\n",dp[M]);
64     return 0;
65 }

 

posted on 2018-07-12 21:53  myx12345  阅读(250)  评论(0编辑  收藏  举报

导航