[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 阅读(143) 评论(0) 编辑 收藏 举报