qoj.6555 Sets May be Good 做题记录
此处应该有「笔记」。link
似乎很难有多项式做法,但是一个强大的线性代数能巧妙地解决这个问题。
原题相当于求解
我们很容易将其表示为
令 \(Q\) 为一个模 \(2\) 意义下的可逆矩阵,那么 \(Qx\) 取遍 \(\mathbb F_2^n\),可以表示为
\(Q/Q^T\) 可以表示为若干个初等变换矩阵乘积,我们相当于可以将 \(A\) 进若干次左乘 \(E\) 和右乘 \(E^T\) 的操作,其中这里的 \(E\) 为一个初等变换矩阵。
我们可以通过以下过程将 \(A\) 变换为一个下双对角矩阵(只有主对角线和所有 \((i + 1, i)\)有值)
-
顺序遍历 \(i = 1\sim n\)。
-
对于所有 \(j \in [i + 1, n]\),由于 \(A_{i, j}\) 与 \(A_{j, i}\) 贡献相同,所以令 \(A_{j, i} \gets A_{j, i} + A_{i, j}\),然后 \(A_{i, j} \gets 0\)。
-
若 \(A_{i + 1, i} = 0\),则找到一个 \(j \in [i + 2, n]\) 满足 \(A_{j, i} = 1\),然后交换第 \(i + 1\) 行和第 \(j\) 行,交换第 \(i + 1\) 列和第 \(j\) 列。
-
若第 \(i\) 列没有 \(1\) 则 continue。否则,此时 \(A_{i + 1}, i = 1\),枚举 \(j = i + 2\sim n\),若 \(A_{j, i} = 1\) 则将第 \(j\) 行异或上第 \(i + 1\) 行,将第 \(j\) 列异或上第 \(i + 1\) 列。
注意到所有值都为 \(0/1\),我们尝试 bitset 优化。唯一一个问题在于如何将一列异或上另一列?注意到将若干行异或上第 \(i + 1\) 行,对应列异或上第 \(i + 1\) 列,相当于 \(E_kE_{k-1}\dots E_2E_1AE_1E_2\dots E_{k - 1}E_k\),等于 \((E_kE_{k-1} \dots E_2E_1)A(E_1E_2\dots E_{k - 1}E_k)\)。所以我们可以先把所有行异或完,再进行列异或。列异或考虑求出那些需要异或的列的编号,用一个 \(01\) 串 \(b\) 表示。考虑第 \(i + 1\) 列所有 \(1\) 所在的行 \(j\),令第 \(j\) 行异或上 \(b\) 即可。
这样我们以 \(\mathcal O(\dfrac {n^3} {\omega})\) 的时间复杂度完成了变换。
然后就很简单了,\(x_i\) 贡献只和 \(x_{i - 1}\) 与 \(x_{i + 1}\) 有关,简单 DP 计数即可。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define uLL unsigned LL
#define fi first
#define se second
#define mkp make_pair
#define pir pair<ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF :
// *p1++)
template <class T>
const inline void rd(T &x) {
char ch;
bool neg = 0;
while (!isdigit(ch = getchar()))
if (ch == '-')
neg = 1;
x = ch - '0';
while (isdigit(ch = getchar())) x = (x << 1) + (x << 3) + ch - '0';
if (neg)
x = -x;
}
const ll maxn = 1010, mod = 998244353, iv = mod - mod / 2;
ll power(ll a, ll b = mod - 2, ll p = mod) {
ll s = 1;
while (b) {
if (b & 1)
s = 1ll * s * a % p;
a = 1ll * a * a % p, b >>= 1;
}
return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline ll mus(const T x, const _T y) { return x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline void sub(T &x, const _T y) { x = x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y ? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y ? x : y; }
ll n, m, f[maxn][2][2];
bitset <maxn> a[maxn], tmp;
int main() {
rd(n), rd(m);
for(ll i = 1; i <= m; i++) {
ll u, v; rd(u), rd(v);
a[u][v] = 1;
}
for(ll i = 1; i < n; i++) {
for(ll j = i + 1; j <= n; j++)
if(a[i][j]) a[j][i] = a[j][i] ^ 1, a[i][j] = 0;
if(!a[i + 1][i]) {
for(ll j = i + 2; j <= n; j++)
if(a[j][i]) {
swap(a[i + 1], a[j]);
for(ll k = 1; k <= n; k++) {
ll x = a[k][i + 1];
a[k][i + 1] = a[k][j];
a[k][j] = x;
}
break;
}
}
if(!a[i + 1][i]) continue;
tmp.reset();
for(ll j = i + 2; j <= n; j++)
if(a[j][i]) a[j] ^= a[i + 1], tmp[j] = 1;
for(ll j = i + 1; j <= n; j++)
if(a[j][i + 1]) a[j] ^= tmp;
}
f[1][0][0] = f[1][1][a[1][1]] = 1;
for(ll i = 2; i <= n; i++)
for(ll j = 0; j < 2; j++)
for(ll k = 0; k < 2; k++)
for(ll l = 0; l < 2; l++)
add(f[i][l][k ^ (j & l & a[i][i - 1])
^ (l & a[i][i])], f[i - 1][j][k]);
printf("%d\n", pls(f[n][0][0], f[n][1][0]));
return 0;
}