bzoj2560 串珠子

Description

  铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
  现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
  铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。 

Input

 标准输入。输入第一行包含一个正整数n,表示珠子的个数。接下来n行,每行包含n个非负整数,用空格隔开。这n行中,第i行第j个数为ci,j。 

Output

 标准输出。输出一行一个整数,为连接方案数对1000000007取模的结果。

Sample Input

3
0 2 3
2 0 4
3 4 0

Sample Output

50

HINT

  对于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  

 

正解:状压dp。

这道题很玄学,感觉还是懵懵懂懂。。今天的出题人太丧病了。。

考虑状压dp,我们把n个点压成二进制数。

我们设两个数组,g[s]表示s状态下的所有情况,即s状态下的点两两之间任意连边(包括不连边的情况),f[s]表示s状态下的合法情况,即使得s状态下所有点连通的合法情况。那么答案就是f[2^n-1]。

然后我们可以很容易地求出g[s],我们求出g[s]后,考虑如何求f[s],f[s]就是g[s]减去所有的不合法情况。那么我们可以枚举s的所有子集,设子集为i,那么不合法的情况就是g[i]*f[s^i],我们减去这些情况,就能求出f[s]了。

枚举子集很玄学。。我反正不知道这是怎么回事,看了网上大神的博客。。

http://www.cnblogs.com/jffifa/archive/2012/01/16/2323999.html

for (int x = S; x; x = (x-1)&S)

大概是这个鬼东西。。大神的证明:

x = (x-1)&S实际上是把S中的0全部忽略,并不断减1的结果,比如S=1011,则x分别为:1011, 1010, 1001, 1000, 0011, 0010, 0001。忽略S中第二位的0其实就是111, 110, 101, 100, 011, 010, 001。

称S中的1所在位为有效位,0所在位为无效位,则x中的无效位必为0,有效位为0或1,比如S=1011,x=1001(有效位加下划线)。-1就是加上-1补码1111…,可以想成把无效位的1先加上去,比如x=1001变成1101,再加有效位的1。由于无效位加完肯定是1,会把有效位的进位“传递”下去,然后再位与S使得无效位变成0,实际就相当于有效位加上1111…,也就是有效位-1。

于是我们就能解决这题了。其实我还没搞懂为什么要这么搞。

 

 1 //It is made by wfj_2048~
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <cstdio>
 7 #include <vector>
 8 #include <cmath>
 9 #include <queue>
10 #include <stack>
11 #include <map>
12 #include <set>
13 #define inf (1<<30)
14 #define il inline
15 #define RG register
16 #define ll long long
17 #define rhl 1000000007
18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
19 
20 using namespace std;
21 
22 ll f[1<<16],g[1<<16],bin[20],a[20][20],n;
23 
24 il int gi(){
25     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
26     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
27 }
28 
29 il void work(){
30     n=gi(),bin[0]=1;
31     for (RG int i=1;i<=n;++i)
32     for (RG int j=1;j<=n;++j) a[i][j]=gi();
33     for (RG int i=1;i<=n;++i) bin[i]=bin[i-1]<<1;
34     for (RG int k=0;k<bin[n];++k){
35     f[k]=1;
36     for (RG int i=1;i<n;++i) if (k&bin[i-1])
37         for (RG int j=i+1;j<=n;++j) if (k&bin[j-1]) f[k]=f[k]*(a[i][j]+1)%rhl;
38     g[k]=f[k]; RG int i=k^(k&-k); //枚举真子集?!
39     for (RG int j=i;j;j=(j-1)&i) f[k]=(f[k]-g[j]*f[k^j]%rhl+rhl)%rhl;
40     }
41     printf("%lld\n",f[bin[n]-1]); return;
42 }
43 
44 int main(){
45     File("bunch");
46     work();
47     return 0;
48 }

 

posted @ 2017-03-04 16:53  wfj_2048  阅读(795)  评论(0编辑  收藏  举报