前缀线性基

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;
}
posted @ 2026-05-15 09:38  kzssCCC  阅读(14)  评论(0)    收藏  举报