Hall 定理还在追杀我
Priestess_SLG · · 题解
先回顾一下 Hall 定理:
对一张二分图
G 而言,设其左部点集合为V_1 ,右部点集合为V_2 ,则其存在大小为|V_1| 的最大匹配当且仅当\forall S\subseteq V_1\Longrightarrow |S|\le |N(S)| 。
这里给出 Hall 定理的一个推论;
对一张二分图
G ,设其左部点集合为V_1 ,右部点集合为V_2 ,则其最大匹配大小为|V_1|-\max\limits_{S\subseteq V_1}(|S|-|N(S)|) 。
对一个左部点集合
考虑对一个二分图,记这个二分图的好集合为其左部点集合的所有子集中,满足
然后考虑 dp。设
初始条件显然是
为了方便转移,这里记
然后考虑转移
先考虑第一个条件:
总结一下,
然后考虑转移
总结一下,
转移这两个数组的时候直接一起转移即可。
最后考虑怎么统计答案。设整张图的极好集合为
总结一下,这一类二分图度对答案的贡献可以表示为
该算法的时间复杂度为
:::success[Code]
namespace lowspeed_song {
inline void init() {
}
int p[610][610], tmp[610][610], all[610][610], noth[610][610], cnt[610], f[610][610], g[610][610];
inline void sol([[maybe_unused]]int __testcase_id) {
int n, m; cin >> n >> m;
const int inv100 = inversion(100);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) cin >> p[i][j], p[i][j] = p[i][j] * inv100 % mod;
for (int i = 0; i < n; i++) {
tmp[i][0] = 1;
for (int t = 1; t < (1 << m); ++t) {
int j = __builtin_ctz(t);
tmp[i][t] = tmp[i][t ^ (1 << j)] * (1 - p[i][j] + mod) % mod;
}
}
for (int t = 0; t < (1 << m); ++t) noth[0][t] = 1;
for (int s = 1; s < (1 << n); ++s) {
int i = __builtin_ctz(s), pre = s ^ (1 << i);
for (int t = 0; t < (1 << m); ++t) noth[s][t] = noth[pre][t] * tmp[i][t] % mod;
}
for (int s = 0; s < (1 << n); ++s) {
all[s][0] = 1;
for (int t = 1; t < (1 << m); ++t) {
all[s][t] = 1;
for (int S = t; S; S = (S - 1) & t) all[s][t] = (all[s][t] + mod - all[s][t ^ S] * noth[s][S] % mod) % mod;
}
}
for (int s = 0; s < (1 << n); ++s)
for (int t = 0; t < (1 << m); ++t) f[s][t] = g[s][t] = 0;
f[0][0] = g[0][0] = 1;
for (int s = 1; s < (1 << n); ++s) {
for (int t = 0; t < (1 << m); ++t) {
if (__builtin_popcount(s) > __builtin_popcount(t)) {
g[s][t] = all[s][t];
for (int a = (s - 1) & s; ; a = (a - 1) & s) {
for (int b = t; ; b = (b - 1) & t) {
if (__builtin_popcount(a) - __builtin_popcount(b) >= __builtin_popcount(s) - __builtin_popcount(t)) g[s][t] = (g[s][t] + mod - g[a][b] * f[s ^ a][t ^ b] % mod * noth[a][t ^ b] % mod) % mod;
if (!b) break;
} if (!a) break;
}
} else {
f[s][t] = all[s][t];
for (int a = s; a; a = (a - 1) & s)
for (int b = t; ; b = (b - 1) & t) {
if (__builtin_popcount(a) > __builtin_popcount(b)) f[s][t] = (f[s][t] + mod - g[a][b] * f[s ^ a][t ^ b] % mod * noth[a][t ^ b] % mod) % mod;
if (!b) break;
}
}
}
}
int res = 0;
for (int s = 0; s < (1 << n); ++s)
for (int t = 0; t < (1 << m); ++t)
for (int t0 = (((1 << m) - 1) ^ t);; t0 = (t0 - 1) & (((1 << m) - 1) ^ t)) {
res = (res + g[s][t] * noth[s][(((1 << m) - 1) ^ t)] % mod * f[((1 << n) - 1) ^ s][t0] % mod * noth[((1 << n) - 1) ^ s][(((1 << m) - 1) ^ t) ^ t0] % mod * (n - __builtin_popcount(s) + __builtin_popcount(t)) % mod) % mod;
if (!t0) break;
}
cout << res << '\n';
}
} // namespace lowspeed_song
:::