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

分析

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

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

容易想到:

\[g[S]=\sum f[S-T] \times 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\n",f[(1<<n)-1]);
	return 0;
}

posted on 2019-02-20 20:23  ErkkiErkko  阅读(141)  评论(0编辑  收藏  举报