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