[清华集训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;
}
posted @ 2021-08-03 15:08  fhq_treap  阅读(61)  评论(0编辑  收藏  举报