首先对于 \(0 / 1\) 和为奇数,转化为异或为 \(1\) 来考虑。
考虑如果已经确定了行的选取,又该如何计数。
考虑对于每一列,都处理好在对应行的位置的异或值。
然后记 \(\operatorname{c}_0, \operatorname{c}_1\) 表示列异或值为 \(0 / 1\) 的数量。
知道了 \(\operatorname{c}_0, \operatorname{c}_1\) 之后,就可以直接根据 \(\operatorname{c}_0, \operatorname{c}_1\) 来计数了。
首先对于 \(0\) 来说随便选都行,方案数就为 \(2^{\operatorname{c}_0}\)。
对于 \(1\) 来说就要求选的数量为奇数个,因为当 \(n\ge 1\) 时,\(\sum\limits_{i = 0}^n (-1)^i\binom{n}{i} = 0\),说明当 \(n\ge 1\) 时 \(\sum\limits_{0\le i\le n, i \bmod 2 = 1} \binom{n}{i} = 2^{n - 1}\),于是可以知道这部分的数量为 \(\begin{cases}2^{\operatorname{c_1} - 1} & \operatorname{c_1}\ge 1\\ 0 & \operatorname{c_1} = 0\end{cases}\)。
两部分相乘,就知道最后的方案数为 \(\begin{cases}2^{m - 1} & \operatorname{c_1}\ge 1 \\ 0 & \operatorname{c}_1 = 0\end{cases}\)。
那么就可以去考虑计数选择行的方案数使得每一列对应异或出来都为 \(0\)(\(\operatorname{c}_1 = 0\))。
这部分是好算的。
考虑异或线性基,方案数就为 \(2^{z}\)(\(z\) 为自由元个数)。
证明考虑每个自由元都对应着一种不相同的线性组合方式,不管怎样组合得到的方案都不一样。
可以用 bitset
优化线性基。
时间复杂度 \(\mathcal{O}(\frac{nm^2}{\omega})\)。
#include<bits/stdc++.h>
using ll = long long;
constexpr ll mod = 998244353;
inline ll qpow(ll b, ll a = 2, ll v = 1) {
while (b) b & 1 && ((v *= a) %= mod), b >>= 1, (a *= a) %= mod;
return v;
}
const int maxn = 3e2 + 10;
std::bitset<maxn> a, w[maxn];
int main() {
int n, m; scanf("%d%d", &n, &m);
ll tot = 1;
for (int T = 1; T <= n; T++) {
for (int i = 1, x; i <= m; i++)
scanf("%d", &x), a.set(i, x);
int f = 1;
for (int i = 1; i <= m; i++) {
if (! a[i]) continue;
if (! w[i][i]) {
f = 0, w[i] = a;
break;
}
a ^= w[i];
}
if (f)
(tot <<= 1) %= mod;
}
printf("%lld\n", (qpow(n) - tot + mod) * qpow(m - 1) % mod);
return 0;
}
标签:Atcoder,ge,int,ll,Solution,异或,cases,Odd,operatorname
From: https://www.cnblogs.com/rizynvu/p/18331138