题目大意
给你n个数,对于一个区间[l,r],xorsum=这个区间所有异或和,min = 区间最小值,max = 区间最大值,要求xorsum ^ min ^ max最大。
解题思路
对于xor最大,我们一般会往两个方向想:一.线性基 二.01字典树
这题是用字典树。如果只是求xorsum最大,我们可以维护一个前缀异或和的字典树,然后每次得到新的前缀异或和,先在字典树查一下能得到的最大值,再插入字典树。但是这里还有最大值和最小值,由于是统计区间信息,所以我们来考虑分治:
对于当前区间[l,r],统计出所有左端点在[l,mid],右端点在[mid+1,r]的区间的信息,然后再递归[l,mid]和[mid+1,r]。那么我们现在需要做的就是快速统计跨越mid的区间信息。这个时候可以分成四种情况:
- 最大值和最小值都在左边
- 最大值和最小值都在右边
- 最大值在左边,最小值在右边
- 最小值在左边,最大值在右边
对于第一种情况,我们在字典树上插入右半边可取的前缀异或和,然后对于每一个左端点查询 它的后缀异或和 ^ min ^ max可以在字典树上合出的最大值。
我们可以从大到小枚举左端点,可以发现左端点越小,右端点的可取范围就越大。所以每次改变左端点之后,就可以在字典树上插入信息。
第二张情况和第一种情况类似
对于第三种情况,最大值要在左边,最小值要在右边,所以右端点可以取的最小值和最大值都有限制,每次移动左端点的时候,要改变右端点范围,并根据范围在字典树上插入和删除值。
第四种情况和第三种类似。
这里有一个trick:可以只统计第一种情况和第三种情况,然后把数组倒过来再统计一遍,可以减少码量。
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 1e5 + 50;
const int inf = 0x3f3f3f3f;
int ch[maxn*40][2], cnt[maxn*40], tot;
int a[maxn], n, ans;
void clr(){
tot = 0; ch[tot][0] = ch[tot][1] = 0; cnt[tot] = 0;
}
void ins(int x){
// cout<<"add:"<<x<<endl;
int rt = 0;
for(int i = 30; i >= 0