题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4671

题解

半年前刚学计数的时候对这道题怀着深深的景仰,现在终于可以来做这道题了。

类似于一般的容斥和反演题,我们发现整个图是联通的图非常不好求。于是我们转化为整个图钦定了有 \(i\) 个块必须不连通,其余任意的方案数。

然后考虑这个怎么求,我们可以暴力枚举一下把这些数分成很多组,显然方案数就时 \(B_n\)(贝尔数,就是 \(\sum\limits_{i=0}^n \begin{Bmatrix}n\\i\end{Bmatrix}\) 的和,在 \(n \leq 10\) 的时候都不超过十万级别)。

然后就是相当于有一些边不能存在,其余的别可以任意存在。考虑用一个线性基来维护。由于边数不超过 \(\frac{n(n-1)}2\),所以可以用 ll 表示。然后问题转化为一个数有多少个子集存在于线性基中。

但是一个数有多少个子集存在于线性基中不太好维护,经过某位同学的提示,可以想到把那些可以任意为 \(0/1\) 的位扔掉,只记录只能为 \(0\) 的位,把这些位扔进线性基。最后只需要用线性基求出有多少种方案使得异或和为 \(0\) 就可以了。


以下是代码,时间复杂度为 \(O(B_nn^2m)\)。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; template<typename I>
inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
template<typename I>
inline void read2(char *s, I &x) {
int f = 0, c;
while (!isdigit(c = *s++)) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = *s++)) x = (x << 1) + (c & 15);
f ? x = -x : 0;
} const int N = 45 + 7;
const int M = 60 + 7; int n, m, sn, ssn;
int a[N], bl[N], ss[N];
ll b[M], f[N], S[N][N];
pii dy[N];
char s[N], p[N]; struct XXJ {
ll a[N];
inline void cls() { memset(a, 0, sizeof(a)); }
inline bool ins(ll x) {
for (int i = ssn - 1; ~i; --i)
if ((x >> i) & 1) {
if (a[i]) x ^= a[i];
else return a[i] = x, 1;
}
return 0;
}
inline int count() {
int cnt = 0;
for (int i = ssn - 1; ~i; --i) if (a[i]) ++cnt;
return cnt;
}
} gg; inline void calc(int y) {
ss[0] = 0;
for (int i = 0; i < sn; ++i)
if (bl[dy[i].fi] != bl[dy[i].se]) ss[++ss[0]] = i;
ssn = ss[0], gg.cls();
for (int i = 1; i <= m; ++i) {
ll c = 0;
for (int j = ss[0]; j; --j) c = c << 1 | ((b[i] >> ss[j]) & 1);
gg.ins(c);
}
f[y] += 1ll << (m - gg.count());
} inline void dfs(int x, int y) {
if (x == n + 1) return calc(y);
for (int i = 1; i <= y + 1; ++i) bl[x] = i, dfs(x + 1, std::max(y, i));
} inline void work() {
dfs(1, 0);
ll ans = 0;
S[0][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j) S[i][j] = S[i - 1][j - 1] + (i - 1) * S[i - 1][j];
for (int i = 1; i <= n; ++i)
if ((i - 1) & 1) ans -= S[i][1] * f[i];
else ans += S[i][1] * f[i];
printf("%lld\n", ans);
} inline void init() {
read(m);
for (int i = 1; i <= m; ++i) {
scanf("%s", s + 1);
int nn = strlen(s + 1);
std::reverse(s + 1, s + nn + 1);
n = (1 + (int)sqrt(1 + 8 * nn)) >> 1;
read2(s + 1, b[i]);
}
sn = 0;
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) dy[sn++] = pii(i, j);
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}

BZOJ4671 异或图 斯特林反演+线性基的更多相关文章

  1. bzoj4671 异或图(斯特林反演,线性基)

    bzoj4671 异或图(斯特林反演,线性基) 祭奠天国的bzoj. 题解时间 首先考虑类似于容斥的东西. 设 $ f_{ i } $ 为至少有 $ i $ 个连通块的方案数, $ g_{ i } $ ...

  2. bzoj4671: 异或图——斯特林反演

    [BZOJ4671]异或图 - xjr01 - 博客园 考虑先算一些限制少的情况 gi表示把n个点的图,划分成i个连通块的方案数 连通块之间不连通很好处理(怎么处理看下边),但是内部必须连通,就很难办 ...

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

    题意 定义两个结点数相同的图 \(G_1\) 与图 \(G_2\) 的异或为一个新的图 \(G\) ,其中如果 \((u, v)\) 在 \(G_1\) 与 \(G_2\) 中的出现次数之和为 \(1 ...

  4. 【bzoj4671】异或图(容斥+斯特林反演+线性基)

    传送门 题意: 给出\(s,s\leq 60\)张图,每张图都有\(n,n\leq 10\)个点. 现在问有多少个图的子集,满足这些图的边"异或"起来后,这张图为连通图. 思路: ...

  5. bzoj 4671 异或图——容斥+斯特林反演+线性基

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4671 考虑计算不是连通图的方案,乘上容斥系数来进行容斥. 可以枚举子集划分(复杂度是O(Be ...

  6. bzoj 4671 异或图 —— 容斥+斯特林反演+线性基

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4671 首先,考虑容斥,就是设 \( t[i] \) 表示至少有 \( i \) 个连通块的方 ...

  7. bzoj4671: 异或图

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

  8. BZOJ4671异或图

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

  9. P5169 xtq的异或和(FWT+线性基)

    传送门 我咋感觉我学啥都是白学-- 首先可以参考一下这一题,从中我们可以知道只要知道两点间任意一条路径以及整个图里所有环的线性基,就可以得知这两个点之间的所有路径的异或和 然而我好像并不会求线性基能张 ...

随机推荐

  1. mysql中各种连接的区别

    现在有如下两个表: A表. B表. 一:使用笛卡尔连接 SELECT * FROM a,b 结果: 二:内连接 SELECT * FROM a INNER JOIN b on a.a_id=b.par ...

  2. JS自定义随机数字键盘

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. [CF1167D]Bicolored RBS题解

    模拟两个颜色的扩号层数,贪心,如果是左括号,哪边的层数浅就放那边:如果是右括号,哪边的层数深就放那边. 至于层数的维护,两个int就做掉了 放个代码: #include <cstdio> ...

  4. 1206C Almost Equal

    题目大意 给你一个n 让你用1~2*n的数填满一个2*n个点的环 使得任意连续的n个位置的和的最大值减最小值不大于1 分析 我们通过瞎jb找规律发现n为偶数吴姐 而n为奇数我们设前n个位置为0组,后n ...

  5. Jupyter Notebook 快捷键(基本)

    Jupyter Notebook 快捷键 Jupyter Notebook 有两种键盘输入模式.编辑模式,允许你往单元中键入代码或文本:这时的单元框线是绿色的.命令模式,键盘输入运行程序命令:这时的单 ...

  6. 使用sqlalchemy创建单条数据-分层管理代码

    这里主要是如何把整个流程的代码分层管理,方便维护 不拆分层次,整个流程顺下来的代码看这里:sqlAlchemy基本使用 项目结构: model.py用来描述表结构: from sqlalchemy i ...

  7. swiper在vue中的用法

    首先通过npm i vue-awesome-swiper --save 来在vue中下载插件 然后再main.js中引入 require('swiper/dist/css/swiper.css')im ...

  8. jenkins持续集成、插件以及凭据

    Jenkins介绍 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. Jenkins功能包括: ...

  9. python selenium无法清除文本框内容问题

    正常是我们在清除文本框内容的时候,都会使用 clear() 函数进行清除,但是有时候会出现,清除完成后再点击查询时,文本框的内容会再次自动填充,这个时候我们可以选择以下方式: #清空查询条件drive ...

  10. MySQL-第三篇SQL语句基础(2)数据库约束

    1.数据库约束. 约束是在表上强制执行的数据校验规则,约束主要用于保证数据库里数据的完整性. MySQL使用information_schema数据库里的TABLE_CONSTRAINTS表来保存该数 ...