2017icpc 西安 A.XOR
2017icpc 西安 A.XOR
题意:
长度为\(n\)的数组,\(q\)次询问,每次询问给出\(l,r\),从\([l,r]\)区间中选出一些数字使得这些数字的异或和或上\(k\)最大。
思路:
线性基是处理异或问题的好手,对于这种区间异或最大可以考虑用线段树维护线性基合并。
考虑怎么样能有\(k|(a_{i_1}\ xor\ a_{i_2}\ xor\ ,...,\ xor\ a_{i_m})\)最大。
最开始的想法是:搞出区间线性基,然后从高位一位一位的搜,如果\(k\)有\(1\),那就不考虑线性基这一位,如果没有\(1\),就一定考虑,但是很快的就举出了反例。
考虑\(k\)需要什么?
当\(k\)这一位有\(1\),我们可以尽量不管他,如果这一位没有\(1\),我们尽量从数字中选择把这位填上,实在没有就算了。
所以对\(k\)取反,对于线段树每个节点插入\(k\&a(i)\),每次询问查询\([l,r]\)最大结果,最后最结果或上\(k\)。
正确性:
当对\(k\)取反后,对\(a(i)\)进行与运算,实际上就是在找\(a(i)\)这一位有没有\(1\)。
同时也有可能将本来\(a(i)\)有\(1\)的地方给取反后的\(k\)搞掉了,这不用担心,因为最后对答案或上\(k\),就回来了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+10;
int n, m, k, a[maxn];
int l, r, ans;
struct LineBase
{
int p[32];
void init() {memset(p, 0, sizeof p);}
void ins(int x)
{
for(int i = 30; i >= 0; i--)
{
if(x&(1<<i))
{
if(!p[i])
{
p[i] = x;
return;
}
x ^= p[i];
}
}
}
int qmax()
{
int res = 0;
for(int i = 30; i >= 0; i--)
res = max(res, res^p[i]);
return res;
}
};
struct SegmentTree
{
int l, r;
LineBase t;
#define lson (p<<1)
#define rson (p<<1|1)
#define l(x) tree[x].l
#define r(x) tree[x].r
#define t(x) tree[x].t
}tree[maxn<<2];
LineBase merge_base(LineBase a, LineBase b)
{
for(int i = 30; i >= 0; i--)
if(a.p[i]) b.ins(a.p[i]);
return b;
}
void build(int p, int l, int r)
{
l(p) = l, r(p) = r;
t(p).init();
if(l == r)
{
t(p).ins(a[l]);
return;
}
int mid = (l+r)>>1;
build(lson, l, mid);
build(rson, mid+1, r);
t(p) = merge_base(t(lson), t(rson));
}
LineBase ask(int p, int l, int r)
{
if(l <= l(p) && r(p) <= r) return t(p);
int mid = (l(p)+r(p))>>1;
if(mid >= r) return ask(lson, l, r);
else if(mid < l) return ask(rson, l, r);
else return merge_base(ask(lson, l, r), ask(rson, l, r));
}
void solve()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
a[i] = a[i]&(~k);
}
build(1, 1, n);
while(m--)
{
scanf("%d%d", &l, &r);
LineBase tmp = ask(1, l, r);
ans = tmp.qmax();
ans = ans|k;
printf("%d\n", ans);
}
}
int main()
{
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}