2019.02.09 bzoj2560: 串珠子(状压dp+简单容斥)

时间:2022-01-11 01:06:30

传送门

题意简述:nnn个点的带边权无向图,定义一个图的权值是所有边的积,问所有nnn个点都连通的子图的权值之和。


思路:

fif_ifi​表示保证集合iii中所有点都连通其余点随意的方案数。

gig_igi​表示只考虑集合iii中所有点的状态的子图的权值和。

我们先预处理出ggg数组,然后考虑递推fff数组。

显然fif_ifi​是等于gig_igi​扣掉一些东西的,扣掉的应该就是不连通的情况。

于是我们枚举编号最小的点所在的连通块来扣掉非法情况。

时间复杂度O(n2n+3n)O(n2^n+3^n)O(n2n+3n)

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=17,mod=1e9+7;
int n,f[1<<N],g[1<<N],val[N][N],bit[N],idx[1<<N];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline int lowbit(const int&x){return x&-x;}
int main(){
	n=read();
	for(ri i=0;i<n;++i)for(ri j=0;j<n;++j)val[i][j]=read();
	bit[0]=1,idx[1]=0;
	for(ri i=1;i<n;++i)bit[i]=bit[i-1]<<1,idx[bit[i]]=i;
	g[0]=1;
	for(ri i=1,x;i<(1<<n);++i){
		g[i]=g[i^lowbit(i)],x=idx[lowbit(i)];
		for(ri j=0;j<n;++j)if((i>>j)&1)g[i]=mul(g[i],add(val[x][j],1));
	}
	g[0]=0;
	for(ri i=1,x;i<(1<<n);++i){
		f[i]=g[i];
		for(ri j=i^lowbit(i),stat=j;~stat;stat=!stat?-1:j&(stat-1))f[i]=dec(f[i],mul(g[j^stat],f[lowbit(i)^stat]));
	}
	cout<<f[(1<<n)-1];
	return 0;
}