【luogu CF1100F】Ivan and Burgers

Ivan and Burgers

题目链接:luogu CF1100F

题目大意

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

思路

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

首先不难想到可有处理出 \(1\sim i\) 的线性基,这个可以用像前缀和的方法搞到。
但问题就是求 \(j\sim 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\n", query(y, x));
	}
	
	return 0;
}
posted @ 2021-07-05 21:41  あおいSakura  阅读(21)  评论(0编辑  收藏  举报