【BZOJ2560】状态压缩+容斥原理

3hours orz orz orz.... BZOJ2560(非权限)

2560: 串珠子

Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 654  Solved: 431 [Submit][Status][Discuss]

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

Source

2012国家集训队Round 1 day1 很容易想到,对于一个集合里面的点直接连或者不连为 (积)(A(i,j)+1) (即边两两选择相连哪条线或者不连直接乘起来)。例如样例就是60. 之后把一些没有连通的方案减去就好啦,是不是很简单?(不存在的) 对于一个集合之后我们会想到枚举子集,我们设定一个标志点,为方便,就设定为1,从这个点开始扩张。先枚举合法的子集,若该子集不包含1,表示不合法跳过,然后再取一个反,得到非法的子集,将非法子集(不连通)与合法子集(部分连通)的方案乘积减去。 建议画图理解。 code
/*
h[i] baolian fangan
f[i] get fangan
*/
#include<stdio.h>
#include<bits/stdc++.h> 
#define lowbit(x) ((x)&(-x))
#define int long long
using namespace std;
const int mod = 1000000007;
int n,S;
int c[20][20];
int h[1<<16],f[1<<16];
int getlow(int x)
{
	int sum=0;
	for(;x;x-=lowbit(x)) ++sum;
	return sum;
}
main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%lld",&c[i][j]);
		}
	} 
	S=(1<<n)-1;
	
	for(int s=1;s<=S;s++)
	{
		h[s]=1;
		for(int j=1;j<n;j++)
		{
			if(!(((1<<(j-1)))&s)) continue;
			for(int k=j+1;k<=n;k++)
			{
				if(!(((1<<(k-1)))&s)) continue;
				h[s]=(long long)h[s]*(c[j][k]+1)%mod;
			}
		}
	}
	for(int s=1;s<=S;s++)
	{
		if(!(s&1)) continue;
		int kao;
		f[s] = h[s];
		for(int c=(s-1)&s;c;c=(c-1)&s)
		{ 
			if(!(c&1)) continue;
			kao = s^c;
			f[s] = (f[s] - (long long)f[c]*h[kao]%mod+mod)%mod; 
		}
	}
	printf("%lld",f[S]);
}
 
posted @ 2018-05-29 17:26  Newuser233  阅读(5)  评论(0)    收藏  举报