BZOJ 2560: 串珠子

网上说是容斥,说简单点就是补集转化,再简单点就是总方案-不合法方案

我们枚举和联通块中编号最小的点联通的点集

联通块为i,枚举的点集为j,首先j必须是联通的,然后j和i^j不能有连边,然后i^j内部是随便连的

这样枚举保证不重不漏

令F[i]表示点集为i必须联通的方案

   G[i]表示点集为i不保证联通的方案

核心思想是枚举和编号最小点联通的块

#include<cstdio>
using namespace std;
const int mod=1e9+7;
int c[25][25],stack[25],F[70005],G[70005],ID[70005];
int lowbit(int x){
	return x&(-x);
}
int main(){
	int n;
	scanf("%d",&n);
	for (int i=0; i<n; i++)
		for (int j=0; j<n; j++)
			scanf("%d",&c[i][j]);
	G[0]=1;
	for (int i=0; i<n; i++) ID[1<<i]=i;
	for (int i=1; i<(1<<n); i++){
		int top=0;
		for (int j=i; j; j-=lowbit(j)) stack[++top]=ID[lowbit(j)];
		G[i]=G[i-lowbit(i)];
		for (int j=2; j<=top; j++) G[i]=1ll*G[i]*(c[stack[1]][stack[j]]+1)%mod;
	}
	for (int i=1; i<(1<<n); i++){
		F[i]=G[i];
		for (int j=(i-1)&i; j; j=(j-1)&i)
			if (j&lowbit(i)){
				F[i]-=1ll*F[j]*G[i^j]%mod;
				(F[i]+=mod)%=mod;
			}
	}
	printf("%d\n",F[(1<<n)-1]);
	return 0;
}

  

 

posted @ 2018-10-28 18:58  ~Silent  阅读(248)  评论(0编辑  收藏  举报
Live2D