【luogu CF1100F】Ivan and Burgers

Ivan and Burgers

题目链接:luogu CF1100F

题目大意

给你一个序列,多次询问。
每次问题序列第 l 个数到第 j 个数中,你可以选任意个异或起来,这个异或的最大值是多少。

思路

不难看到有这种各样的方法。
我这里用的是比较优的纯线性基 (O(nlogn)) 做法。

首先不难想到可有处理出 (1sim i) 的线性基,这个可以用像前缀和的方法搞到。
但问题就是求 (jsim i) 的要怎么搞。
容易想到,我们要让后面的数更多的放在线性基的数组中。

那我们线性基不仅有数组 (d_i),还要有一个 (tim_i) 表示这是序列中的第几个数插进去的。
那我们插入的时候,原本没有数就放进去,有的话就比较,如果现在插入的在后面,就 swap 一下,然后继续。
swap 相当于把现在的数插进去,然后把之前插进入的拿出来,重新跟后面匹配。

那这样,如果后面的可以自己组合替代前面的,那前面的就不会被放上去。
那我们就可以对于 ([l,r]),我们查询 ([1,r]) 的线性基,如果它异或了更优而且时间在 (l) 之后或者是 (l) 我们再异或,这样得出来的答案就是 ([l,r]) 的答案了。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

int n, d[500001][21], tim[500001][21];
int q, x, y;

void add(int x, int y) {
	int nt = x;
	for (int i = 20; i >= 0; i--)
		if ((y >> i) & 1) {
			if (!d[x][i]) {
				d[x][i] = y;
				tim[x][i] = nt;
				break;
			}
			if (nt > tim[x][i]) {//优先把时间后的放进去
				swap(nt, tim[x][i]);
				swap(d[x][i], y);
			}
			y ^= d[x][i];
		}
}

int query(int x, int y) {
	int re = 0;
	for (int i = 20; i >= 0; i--)
		if (tim[x][i] >= y && (re ^ d[x][i]) > re)//规定找在这个时间之后的
			re ^= d[x][i];
	return re;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		for (int j = 0; j <= 20; j++)
			d[i][j] = d[i - 1][j], tim[i][j] = tim[i - 1][j];
		add(i, x);//搞出 1~i 的线性基(前缀和的感觉)
	}
	
	scanf("%d", &q);
	while (q--) {
		scanf("%d %d", &x, &y);
		printf("%d
", query(y, x));
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1100F.html