[清华集训2012]串珠子
是一个很经典集合问题。
在一个点集中,一定可以分割成若干个联通子集。
如果我们设 \(f_i\) 为一个点集的联通方案,\(g_i\) 为一个点集的所有方案。
我们先任取一个点,考虑枚举一个集合和这个点不联通。
那么我们知道这样不连通的方案 \(\sum g[j] \times\ f[i\ xor\ j]\)。
依据简单的容斥:
\(f_i = g_i - \sum_{s\in(i\ xor\ j)}\ g[j] \times\ f[i\ xor\ j]\)。
答案为 \(f_{2^n - 1}\)。
在实现有细节,即先把当前集合丢掉 \(lowbit(i)\),在枚举不连通的子集。
#include<iostream>
#include<cstdio>
#define ll long long
#define mod 1000000007
#define N 20
#define M (1 << 17)
ll n;
ll c[N][N];
ll f[M],g[M];
int main(){
scanf("%lld",&n);
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
scanf("%lld",&c[i - 1][j - 1]);
ll K = (1 << n) - 1;
for(int i = 0;i <= K;++i){
g[i] = 1;
for(int j = 0;j <= n - 1;++j){
for(int k = j + 1;k <= n - 1;++k){
if(((i >> j) & 1) && ((i >> k) & 1))
g[i] = (g[i] * (c[j][k] + 1)) % mod;
}
}
}
for(int i = 0;i <= K;++i){
f[i] = g[i];
int t = i ^ (i & -i);
for(int j = t;j > 0;j = (j - 1) & t){
f[i] = (f[i] - g[j] * f[i ^ j] % mod + mod) % mod;
}
}
std::cout<<f[K]<<std::endl;
}