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

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


思路:
f i f_i fi表示保证集合 i i i中所有点都连通其余点随意的方案数。
g i g_i gi表示只考虑集合 i i i中所有点的状态的子图的权值和。
我们先预处理出 g g g数组,然后考虑递推 f f f数组。
显然 f i f_i fi是等于 g i g_i gi扣掉一些东西的,扣掉的应该就是不连通的情况。
于是我们枚举编号最小的点所在的连通块来扣掉非法情况。
时间复杂度 O ( n 2 n + 3 n ) O(n2^n+3^n) O(n2n+3n)
代码:

#include
#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;
}

你可能感兴趣的:(#,容斥原理,#,状压dp,#,dp)