Description:
定义两个结点数相同的图
G1
G
1
与图
G2
G
2
的异或为一个新的图
G
G
, 其中如果在
G1
G
1
与
G2
G
2
中的出现次数之和为
1
1
, 那么边在
G
G
中, 否则这条边不在中.
现在给定
s
s
个结点数相同的图, 设
S=G1,G2,...,Gs
S
=
G
1
,
G
2
,
.
.
.
,
G
s
, 请问
S
S
有多少个子集的异
或为一个连通图?
Solution:
直接枚举不可做,那么考虑容斥,计算至少有几个连通块进行容斥,设方案数为。那么结果就是恰好有i个连通块,那么就是
gi
g
i
。
那么花费
bell(n)
b
e
l
l
(
n
)
的时间划分子集,这些子集之间不能有边,利用线性基算出自由元数量
x
x
,那么对应的方案数就是。
考虑
gi
g
i
和
fi
f
i
的关系,有
fm=∑ni=mgi∗S(i,m)
f
m
=
∑
i
=
m
n
g
i
∗
S
(
i
,
m
)
根据斯特林反演
gm=∑ni=m(−1)i−m∗s(i,m)∗fi
g
m
=
∑
i
=
m
n
(
−
1
)
i
−
m
∗
s
(
i
,
m
)
∗
f
i
那么得出
g1=∑ni=1(−1)i−1∗(i−1)!∗fi
g
1
=
∑
i
=
1
n
(
−
1
)
i
−
1
∗
(
i
−
1
)
!
∗
f
i
所以每次计算出
fi
f
i
,然后套入式子即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 11, maxm = 65;
int n, m;
long long ans;
int g[maxm][maxn][maxn], bl[maxn];
long long a[maxm], fac[maxn];
char s[maxm];
void dfs(int dep, int k) {
if(dep == n) {
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; ++i) {
for(int j = i + 1; j <= n; ++j) {
if(bl[i] != bl[j]) {
long long tmp = 0;
for(int u = 1; u <= m; ++u) {
tmp |= 1LL * g[u][i][j] << (u - 1);
}
for(int t = m - 1; ~t; --t) {
if(tmp >> t & 1) {
if(a[t]) {
tmp ^= a[t];
} else {
a[t] = tmp;
break;
}
}
}
}
}
}
int c = 0;
for(int i = 0; i < m; ++i) {
c += a[i] > 0;
}
ans += ((k & 1) ? 1 : -1) * (1LL << (m - c)) * fac[k - 1];
return;
}
for(int i = 1; i <= k + 1; ++i) {
bl[dep + 1] = i;
dfs(dep + 1, max(k, i));
}
}
int main() {
scanf("%d", &m);
for(int i = 1; i <= m; ++i) {
scanf("%s", s + 1);
int l = strlen(s + 1), t = 0;
n = (1 + sqrt(1 + (l << 3))) / 2;
for(int u = 1; u <= n; ++u) {
for(int v = u + 1; v <= n; ++v) {
g[i][u][v] = s[++t] - '0';
}
}
}
fac[0] = 1;
for(int i = 1; i <= n; ++i) {
fac[i] = fac[i - 1] * i;
}
dfs(0, 0);
printf("%lld\n", ans);
return 0;
}