[UOJ91] [集训队互测2015] 最大异或和
题目大意
给出一个长为 \(n\) 的序列,要求实现三种操作,分别为区间异或上某数,区间覆盖为某数,和询问全局选出若干个数能获得的最大异或和。所有出现的数均不超过 $ 2^m $,操作数不超过 \(q\)。\(n,m,q\leq 2000\)。
分析题意
观察到询问操作所需的就是维护序列的线性基。注意到对于线性基中的两个数 \(x,y\),如果将 \(x\) 变为 \(x\oplus y\),则线性基所能构成的数的集合不变。于是考虑维护差分序列的线性基,修改操作变为修改线性基中的某个元素,和从线性基中删除差分序列的一个区间。删除操作可以暴力删除,因为插入线性基的数总个数是 \(O(n+q)\) 量级的。于是修改操作又转化为维护支持插入一个数和删除一个数的线性基。
带删线性基
题目并没有要求强制在线,所以可以离线并记录下每个插入的元素的删除时间。对于插入的某个元素 \(x\),它会异或上当前线性基基底的若干个数。如果线性基基底所对应的元素先于 \(x\) 删除,就不得不消去基底对 \(x\) 的影响,因而十分麻烦;反之,则直接删去 \(x\) 即可。于是,当 \(x\) 的删除时间晚于它将要异或上的基底时,不妨直接交换 \(x\) 与基底,并继续插入的流程。
时间复杂度
\(O(\dfrac{(n+m)\times m\times q}{\omega})\),瓶颈在于修改操作的转化和线性基的插入。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 2010;
int n, m, Q, last[MAXN];
bitset<MAXN> a[MAXN], b[MAXN];
pair<bitset<MAXN>, int> bs[MAXN];
vector<pair<bitset<MAXN>, int>> vec[MAXN];
struct query {
int opt, l, r;
bitset<MAXN> w;
} q[MAXN];
void insert(pair<bitset<MAXN>, int> cur) {
for (int i = m - 1; i >= 0; --i) {
if (!cur.first.test(i)) continue;
if (bs[i].second == -1) {
bs[i] = cur;
break;
} else {
if (bs[i].second < cur.second) swap(cur, bs[i]);
cur.first ^= bs[i].first;
}
}
}
bitset<MAXN> ask() {
bitset<MAXN> ret;
for (int i = m - 1; i >= 0; --i) {
if (!ret.test(i) && bs[i].second != -1)
ret ^= bs[i].first;
}
return ret;
}
void print(const bitset<MAXN> &x) {
for (int i = m - 1; i >= 0; --i) cout << x.test(i);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> Q;
for (int i = 1; i <= n; ++i) cin >> a[i], b[i] = a[i] ^ a[i - 1];
for (int i = 1; i <= Q; ++i) {
cin >> q[i].opt;
if (q[i].opt == 1) {
cin >> q[i].l >> q[i].r >> q[i].w;
for (int j = q[i].l; j <= q[i].r; ++j) a[j] ^= q[i].w;
} else if (q[i].opt == 2) {
cin >> q[i].l >> q[i].r >> q[i].w;
for (int j = q[i].l; j <= q[i].r; ++j) a[j] = q[i].w;
}
if (q[i].opt <= 2) {
for (int j = q[i].l; j <= min(q[i].r + 1, n); ++j) {
bitset<MAXN> tmp = a[j] ^ a[j - 1];
if (tmp != b[j]) {
if (b[j].any()) vec[last[j]].emplace_back(b[j], i);
b[j] = tmp, last[j] = i;
}
}
}
}
for (int i = 1; i <= n; ++i) if (b[i].any()) vec[last[i]].emplace_back(b[i], Q + 1);
for (int i = 0; i < m; ++i) bs[i].second = -1;
for (int i = 0; i <= Q; ++i) {
for (int j = 0; j < m; ++j) if (bs[j].second == i) bs[j].second = -1;
for (auto &j : vec[i]) insert(j);
if (q[i].opt == 3) print(ask()), cout << "\n";
}
return 0;
}