BZOJ4671 异或图

洛谷传送门

DarkBZOJ 传送门

\(f_i\) 为钦定 \(i\) 个集合两两无边的方案数(即钦定有 \(i\) 个连通块的方案数),设 \(g_i\) 为恰好有 \(i\) 个连通块的方案数,则:

\[f_i = \sum\limits_{j = i}^n {j \brace i} g_j \]

根据斯特林反演,得:

\[g_i = \sum\limits_{j = i}^n (-1)^{j - i} \begin{bmatrix} j \\ i \end{bmatrix} f_j \]

所以:

\[ans = g_1 = \sum\limits_{i = 1}^n (-1)^{i - 1} (i - 1)! f_i \]

问题转化为求 \(f_i\)

发现 \(n\) 很小,考虑直接枚举哪些点被分到了一个集合(这里的枚举量是贝尔数级别的),设有 \(m\) 个集合。那么每一条两端所属集合不同的边都必须被选偶数次。

设编号为 \(b_{i, 1}, b_{i, 2}, \ldots, b_{i, k}\) 的图包含第 \(i\) 条两端所属集合不同的边,\(a_i\) 为第 \(i\) 个图是否在子集中,那么会得到一个形如 \(\forall i, a_{b_{i, 1}} \oplus a_{b_{i, 2}} \oplus \cdots \oplus a_{b_{i, k}} = 0\) 的异或方程组,高斯消元求其自由元个数 \(c\),那么这种划分方案对 \(f_m\)\(2^c\) 的贡献。

总时间复杂度 \(O(B_n n^2 (s + n^2))\)

code
// Problem: P10591 BZOJ4671 异或图
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P10591
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

ll n, m, K, id[99], f[99], b[59], tot;
bool a[99][19][19];
char s[999];

inline ll calc() {
	ll cnt = m;
	for (int i = 1, cur = 1; i <= tot && cur <= m; ++cur) {
		for (int j = i; j <= tot; ++j) {
			if (b[j] & (1LL << cur)) {
				swap(b[i], b[j]);
				break;
			}
		}
		if ((~b[i]) & (1LL << cur)) {
			continue;
		}
		--cnt;
		for (int j = i + 1; j <= tot; ++j) {
			if (b[j] & (1LL << cur)) {
				b[j] ^= b[i];
			}
		}
		++i;
	}
	return cnt;
}

void dfs(int d) {
	if (d > n) {
		tot = 0;
		for (int i = 1; i <= n; ++i) {
			for (int j = i + 1; j <= n; ++j) {
				if (id[i] == id[j]) {
					continue;
				}
				b[++tot] = 0;
				for (int k = 1; k <= m; ++k) {
					if (a[k][i][j]) {
						b[tot] |= (1LL << k);
					}
				}
			}
		}
		f[K] += (1LL << calc());
		return;
	}
	id[d] = ++K;
	dfs(d + 1);
	--K;
	for (int i = 1; i <= K; ++i) {
		id[d] = i;
		dfs(d + 1);
	}
}

void solve() {
	scanf("%lld", &m);
	for (int i = 1; i <= m; ++i) {
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		for (int j = 1;; ++j) {
			if (j * (j - 1) / 2 == len) {
				n = j;
				break;
			}
		}
		int tot = 0;
		for (int j = 1; j <= n; ++j) {
			for (int k = j + 1; k <= n; ++k) {
				a[i][j][k] = s[++tot] - '0';
			}
		}
	}
	dfs(1);
	ll ans = 0, p = 1;
	for (int i = 1; i <= n; ++i) {
		ans += ((i & 1) ? 1 : -1) * p * f[i];
		p *= i;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2024-06-17 18:17  zltzlt  阅读(17)  评论(0编辑  收藏  举报