bzoj 2560: 串珠子【状压dp】

正难则反,设g[s]为集合s不一定联通的方案数,这个很好求,把边数+1乘起来即可,f[s]为s一定联通的方案数
f考虑容斥,就是g[s]-Σf[nw]*g[s^nw],nw是s的子集,这样就减掉了不联通的情况
这个枚举子集的方法还挺巧的……

#include<iostream>
#include<cstdio>
using namespace std;
const int N=20,mod=1000000007;
int n,c[N][N],a[N],tot,f[100005],g[100005];
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			scanf("%d",&c[i][j]);
	for(int s=1,len=(1<<n);s<len;s++)
	{
		tot=0;
		for(int i=0;i<n;i++)
			if(s&(1<<i))
				a[++tot]=i;
		g[s]=1;
		for(int i=1;i<=tot;i++)
			for(int j=i+1;j<=tot;j++)
				g[s]=1ll*g[s]*(c[a[i]][a[j]]+1)%mod;
		f[s]=g[s];//cerr<<g[s]<<endl;
		for(int i=1,le=(1<<tot);i<le;i++)
		{
			int nw=0;
			for(int j=1;j<=tot;j++)
				if(i&(1<<(j-1)))
					nw|=(1<<a[j]);
			f[s]=(f[s]-1ll*f[nw]*g[s^nw]%mod)%mod;
		}
        int nw=0; 
        for(int i=0;i<n;i++) 
            if(s&(1<<i)) 
			{ 
				nw=(s^(1<<i)); 
				break;
			}
        f[s]=g[s];
        for(int i=nw;i;i=nw&(i-1))
            f[s]=(f[s]-1ll*g[i]*f[s^i]%mod)%mod;
	}
	printf("%d\n",(f[(1<<n)-1]+mod)%mod);
	return 0;
}
posted @ 2018-10-09 21:11  lokiii  阅读(181)  评论(0编辑  收藏  举报