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 @   lokiii  阅读(185)  评论(0编辑  收藏  举报
编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· 赶AI大潮:在VSCode中使用DeepSeek及近百种模型的极简方法
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
点击右上角即可分享
微信分享提示