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;
}