
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1577.html
题意
给定一个长度为 n 的序列。
有 m 组询问,每一组询问给出 L,R,k ,询问 L,R 区间内是否能找出一些数,使它们 XOR 起来等于 k 。
$n,m\leq 5\times 10^5, 0\leq a_i,k< 2^{30}$
题解
由于 $n,m$ 同阶,所以以下时间复杂度描述时,对于 $n,m$ 不加区分。
线性基合并是 $O(\log ^2 a_i)$ 的。
直接线段树维护区间线性基或者 ST 表复杂度均为 $O(n\log ^3 a_i)$ 。
CDQ分治时间复杂度为 $O(n\log ^2 a_i)$ 。
以上算法均不能通过。
考虑将询问离线,按照 R 从小到大排序。
我们将 $a_i$ 从左到右依次加入。利用线性基维护尽量靠右的基向量即可(经典套路)。
时间复杂度为 $O(n\log a_i)$ 。
代码
#include <bits/stdc++.h>
using namespace std;
int read(){
int x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
const int N=500005;
int n,m;
int a[N];
struct xxj{
int v[30],p[30];
void clear(){
memset(v,0,sizeof v);
memset(p,0,sizeof p);
}
void insert(int x,int y){
for (int i=29;i>=0;i--)
if (~x>>i&1)
continue;
else if (!v[i]){
v[i]=x,p[i]=y;
break;
}
else {
if (y>p[i])
swap(y,p[i]),swap(x,v[i]);
x^=v[i];
}
}
int query(int x,int y){
for (int i=29;i>=0;i--)
if (x>>i&1)
if (!v[i]||p[i]<y)
return 0;
else
x^=v[i];
return 1;
}
}xianxingji;
struct Query{
int L,R,k,id,ans;
}q[N];
bool cmpR(Query a,Query b){
return a.R<b.R;
}
bool cmpid(Query a,Query b){
return a.id<b.id;
}
int main(){
n=read();
for (int i=1;i<=n;i++)
a[i]=read();
m=read();
for (int i=1;i<=m;i++){
q[i].L=read();
q[i].R=read();
q[i].k=read();
q[i].id=i;
}
sort(q+1,q+m+1,cmpR);
xianxingji.clear();
for (int i=1,j=0;i<=m;i++){
while (j<q[i].R)
j++,xianxingji.insert(a[j],j);
q[i].ans=xianxingji.query(q[i].k,q[i].L);
}
sort(q+1,q+m+1,cmpid);
for (int i=1;i<=m;i++)
puts(q[i].ans?"YES":"NO");
return 0;
}