题意
- 定义两个结点数相同的图 G 1 G_1 G1 与 G 2 G_2 G2 的异或为一个新的图 G G G, 其中如果 ( u , v ) (u, v) (u,v) 在 G 1 G_1 G1 与 G 2 G_2 G2 中的出现次数之和为 1 1 1, 那么边 ( u , v ) (u, v) (u,v) 在 G G G 中, 否则这条边不在 G G G 中。现在给定 s s s 个结点数均为 n n n 的图 G 1 , . . . , G s G_1, ..., G_s G1,...,Gs ,设 S = G 1 , . . . , G s S = G_1 , ..., G _s S=G1,...,Gs , 求 S S S 有多少个子集的异或为一个连通图。
对于这种连通图计数的问题我们一般考虑反面,即计算不连通图的个数,我们通过枚举集合划分把整个图分成至少有多少个联通块,也就是不同集合间一定不存在边,假设整个图中有 m m m个联通块,那么我们可以写出推容斥系数的式子:
∑ i = 1 m { m i } f i = [ m = 1 ] \sum_{i = 1}^m\begin{Bmatrix} m \\ i \end{Bmatrix}f_i=[m=1] i=1∑m{mi}fi=[m=1]上面这个式子考虑一下实际意义就是分出来的大集合因为内部是随便连的,所以还有可能被分成很多小集合,所以我们要让大于 1 1 1的联通块算的总次数为 0 0 0,就有了上面这个式子。
打表后可以发现 f i = ( − 1 ) i − 1 ( i − 1 ) ! f_i=(-1)^{i-1}(i-1)! fi=(−1)i−1(i−1)!,或者也可以用斯特林反演来算,那么我们现在就是要算这些图有多少个子集,满足跨过集合的边异或次数为偶数,实际上这个也很好算,我们把一个图看成一个二进制数,把所有的图都插入线性基内,设线性基里面元素个数为 x x x,那么有 2 s − x 2^{s-x} 2s−x种方案满足不存在跨过集合的边,这个东西很好理解,加入一个元素不能被线性基的元素异或出来,它在线性基中肯定会占一个位置,然后就没了。
#include <bits/stdc++.h>
#define ll long long
#define For(i, a, b) for (int i = a; i <= b; ++ i)
#define Forr(i, a, b) for (int i = a; i >= b; -- i)
using namespace std;
const int N = 60 + 7;
int m, s, n, len;
ll SS[N], f[N], fac[N]; string S[N];
ll ans;
int be[N];
vector<int> in[N];
void judge() {
ll tmp[N], fuck = (1ll << len) - 1;
int cnt = 0;
For(i, 1, n) For(j, i + 1, n) {
if (be[i] == be[j]) fuck -= 1ll << cnt;
++ cnt;
}
ll b[N] = {cnt = 0};
For(i, 1, s) {
tmp[i] = SS[i] & fuck;
Forr(j, len - 1, 0) if (1ll << j & tmp[i]) {
if (!b[j]) {
++ cnt;
b[j] = tmp[i];
break;
}
tmp[i] ^= b[j];
}
}
ans += pow(2, s - cnt) * f[m];
}
void dfs(int x) {
if (x > n) return judge();
be[x] = ++ m, in[m].push_back(x);
dfs(x + 1);
in[m --].pop_back();
For(i, 1, m) {
be[x] = i, in[i].push_back(x);
dfs(x + 1);
in[i].pop_back();
}
}
int main() {
#ifdef ylsakioi
freopen("bzoj4671.in", "r", stdin);
freopen("bzoj4671.out", "wd", stdout);
#endif
cin >> s;
For(i, 1, s) {
cin >> S[i], reverse(S[i].begin(), S[i].end());
len = S[i].size();
For(j, 0, len - 1) SS[i] = SS[i] << 1 | (S[i][j] ^ 48);
}
n = (sqrt(8 * len + 1) + 1) / 2;
f[1] = fac[1] = 1;
For(i, 2, n) {
f[i] = f[i - 1] * -1 * (i - 1);
fac[i] = 1ll * fac[i - 1] * i;
}
dfs(1);
cout << ans << endl;
return 0;
}