前缀线性基
https://codeforces.com/contest/1100/problem/F
题意概述
给定长度为 \(n\) 的数组 \(c\),\(q\) 个查询,每个查询求 \([l,r]\) 范围内任意选数异或的最大值。
\(1 \le n \le 5 \times 10^5\) ,\(1 \le q \le 5 \times 10^5\) ,\(0 \le c_i \le 10^6\) 。
思路
构造前缀线性基,存储基向量 \(f[i][j]\) 和对应的数组下标 \(pos[i][j]\) 。考虑构造 \(n\) 个版本的线性基,每次先复制前一个版本,然后将 \(c_i\) 插入,插入规则遵循后来者优先。
具体的,记当前值为 \(x\),对应下标为 \(id\) 。枚举每个二进制位 \(j\),如果该位没有值,直接插入;否则判断 \(id\) 是否大于 \(pos[i][j]\) ,如果是,交换 \(f[i][j]\) 和 \(x\) 以及 \(pos[i][j]\) 和 \(id\)。继续消元插入剩下的值。
查询区间 \([l,r]\) 的时候,使用 \(f[r]\) 版本的线性基,只使用 \(pos[r][j] \ge l\) 的基向量。
解释如下:
由于查询使用 \(f[r]\) 版本的线性基,\(pos[r][j]\) 一定是越大越好,因此插入规则遵循后来者优先。
插入的过程中,当产生基向量替换时(\(id > pos[i][j]\)),被换出来的基向量还有剩余价值,要继续消元填充线性基。
时间复杂度 \(\mathcal{O}(32(n+q))\) 。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
int n;
cin >> n;
vector<int> c(n+1);
for (int i=1;i<=n;i++){
cin >> c[i];
}
vector<vector<int>> f(n+1,vector<int>(32)),pos(n+1,vector<int>(32));
for (int i=1;i<=n;i++){
f[i] = f[i-1];
pos[i] = pos[i-1];
int x = c[i];
int id = i;
for (int j=31;j>=0;j--){
if (x>>j&1){
if (f[i][j]==0){
f[i][j] = x;
pos[i][j] = id;
break;
}
if (id>pos[i][j]){
swap(x,f[i][j]);
swap(id,pos[i][j]);
}
x^=f[i][j];
}
}
}
int q;
cin >> q;
while (q--){
int l,r;
cin >> l >> r;
int mx = 0;
for (int j=31;j>=0;j--){
if (f[r][j]!=0 && pos[r][j]>=l){
mx = max(mx,mx^f[r][j]);
}
}
cout << mx << '\n';
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号