BZOJ4671 异或图(容斥+线性基)

题意

定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G ,其中如果 (u,v)G1G2 中的出现次数之和为 1 , 那么边 (u,v)G 中, 否则这条边不在 G 中.

现在给定 s 个结点数相同的图 G1...s , 设 S=G1,G2,,Gs , 请问 S 有多少个子集的异或为一个连通图?

n10,s60

题解

原来听过这题,但一直没有想去写,又讲了一遍,就来做了下,可是还不会。。。

连通图计数的一个经典思路就是容斥。

对于这道题,我们先用贝尔数 bell(n) 的时间来枚举 n 个点的子集(联通块)划分,强制连通性 至少 是这个划分。

也就是说,不同子集的两个点之间一定没有边,相同子集的两个点则任意。

对于一个有 m 个连通块的图。令 fi 为至少有 i 个联通块的容斥系数需要满足

i=1m{mi}fi=[m=1]

可以斯特林反演,也可以打表找规律得出

fi=(1)i1(i1)!

那么问题就转化成,我们只考虑不同子集中的边。对于 s 个边集,有多少种异或方案使得异或和为 0

这个显然是可以利用线性基得到异或方案的,记线性基的元素个数为 tot ,由于之中的元素是线性无关的,其他的 2stot 个集合是一定可以通过异或(或不异或)线性基里的某些元素得到 0 的。

那么方案数其实就是 2stot

因为此题卡常,所以要卡一些常数才能通过此题qwq 具体可以看代码实现优化。

代码

#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("4671.in", "r", stdin); freopen ("4671.out", "w", stdout); #endif } typedef long long ll; const int N = 12, M = 62; int Strl[N][N], id[N][N], n, s, fac[N], coef[N]; bitset<N> E[M][N]; char str[M]; ll base[M], ans; int bel[N]; pair<int, int> ins[M]; void Dfs(int u, int tot) { if (u > n) { Set(base, 0); int res = 0, tmp = -1; For (i, 1, n) For (j, i + 1, n) if (bel[i] != bel[j]) { ins[++ tmp] = make_pair(i, j); } For (i, 1, s) { ll now = 0; For (j, 0, tmp) if (E[i][ins[j].first][ins[j].second]) now |= 1ll << j; Fordown (j, tmp, 0) if (now >> j & 1) { if (base[j]) now ^= base[j]; else { base[j] = now; ++ res; break; } } } ans += coef[tot] * (1ll << (s - res)); return; } For (i, 1, tot + 1) bel[u] = i, Dfs(u + 1, max(tot, i)); } int main () { File(); s = read(); scanf ("%s", str + 1); int len = strlen(str + 1); while (n * (n - 1) / 2 < len) ++ n; For (k, 1, s) { len = 0; For (i, 1, n) For (j, i + 1, n) E[k][i][j] = str[id[i][j] = ++ len] - '0'; if (k < s) scanf ("%s", str + 1); } fac[0] = 1; For (i, 1, n) { fac[i] = 1ll * fac[i - 1] * i; coef[i] = (i & 1 ? 1 : -1) * fac[i - 1]; } Dfs(1, 0); printf ("%lld\n", ans); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10246734.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(290)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示