[BZOJ2560]串珠子:状压DP+容斥原理

分析

为什么我去年6月做过这道题啊,估计当时抄的题解。

具体做法就是令(f[S])表示保证连通点集(S)的方案数,(g[S])表示不保证连通点集(S)的方案数。

容易想到:

[g[S]=sum f[S-T] imes g[T] ]

这里的(T)(S)去掉一个点后得到的集合的所有非空子集。

然后就有:

[f[S]=cnt[S]-g[S] ]

其中(cnt[S])表示在点集(S)中随意连边的的方案数,可以在过程中递推算出。

计算出(f[S])之后,需要(g[S]=g[S]+f[S])

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
#define lowbit(x) ((x)&(-(x)))
#define r (s^t)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=20;
const LL MOD=1e9+7;

int n;
LL c[MAXN][MAXN];
LL f[1<<16],g[1<<16],cnt[1<<16];

inline int calc(int x,int y){
	int xx=0,yy=0;LL ret=1;
	while(x){
		++xx;
		x>>=1;
	}
	while(y){
		++yy;
		if(y&1) ret=(ret*(c[xx][yy]+1))%MOD;
		y>>=1;
	}
	return ret;
}

int main(){
	n=read();
	rin(i,1,n) rin(j,1,n) c[i][j]=read(),f[1<<(i-1)]=g[1<<(i-1)]=1,cnt[1<<(i-1)]=1;
	rin(s,1,(1<<n)-1){
		if(__builtin_popcount(s)<=1) continue;
		int x=lowbit(s),y=(s^x);
		cnt[s]=cnt[y]*calc(x,s)%MOD;
		for(register int t=y;t;t=((t-1)&y)) g[s]=(g[s]+f[r]*g[t])%MOD;
		f[s]=(cnt[s]-g[s]+MOD)%MOD;
		g[s]=(g[s]+f[s])%MOD;
	}
	printf("%lld
",f[(1<<n)-1]);
	return 0;
}
原文地址:https://www.cnblogs.com/ErkkiErkko/p/10409013.html