记 表示数组前
个数的异或和,
就是区间
的异或和
那如果想知道以 位置结尾的子数组的最大异或和,只需要知道
和
中的数异或的最大值就可以了。
(即这些区间异或和的最大值)
那怎么样快速地知道 和
中的数异或和的最大值呢?
我们利用前缀树的结构,对每一个,从最高位到最低位判断。这里需要注意一下,因为我们想让异或和最大,所以我们希望符号位是0,即异或出的数值是正数,然后其他位置我们期望数值为1
所以对于最高位来说,我们希望和的这一位相同,其他位置我们希望和
的这一位不同。
走完前缀树以后,就维护出了以位置结尾的子数组的最大异或和,然后把当前的数加到前缀树里。
#include<iostream>
using namespace std;
struct node;
struct node{
node* num[2];
};
node * head;
void add(int num){//把num加到前缀树里
node * now = head;
for(int i = 31 ; i >= 0 ; i--){
int t = 1&(num>>i);//取num的第i位
if(now->num[t] == NULL){
now->num[t] = new node{NULL,NULL};
now = now->num[t];
}
else{now = now->num[t];}
}
}
const int MAXN = 1e5+10;
int main(){
int n; cin >> n;
int XOR[MAXN]={0};
for(int i =1 ; i <= n ; i++){//求前缀异或和
int t; cin >>t;
XOR[i] = t^XOR[i-1];
}
head = new node{NULL,NULL};
node * now = head;
add(XOR[1]);
int maxn = XOR[1];
for(int i = 2 ; i <= n ; i++){
now = head;
int ans = 0;
for(int j = 31 ; j >= 0 ; j--){
int t = 1&(XOR[i]>>j);
int target= !t;//希望和0还是和1异或
if(j == 31){target = t;}
if(now->num[target]!=NULL){
now=now->num[target];
ans=ans|((target^t)<<j);
}
else{
now = now->num[!target];
ans=ans|((!target^t)<<j);
}
}
add(XOR[i]);
maxn = max(maxn,ans);
}
cout<<maxn<<endl;
return 0;
}

京公网安备 11010502036488号