题意:给出n个数字(n<=1e6),求出一段区间[ l , r ]使得这段区间数字的抑或和最大。如果有多个最大区间,输出字典序最小的一个。
题解:枚举区间右端点 r ,那么左端点 l 一定比 r 小,同时[ l , r ]的抑或和可以表示成 [ 1, r ]xor[ 1 , l-1 ],那么我们枚举一个 r,求出[ 1, r]的和,然后就是查询[1, 1 ][ 1, 2 ] [1 , 3 ]……[1, r-1]中和[1,r]抑或最大的那个值。这个可以通过01字典树完成,然后还要求字典序最小,字典树终点处记一个l位置,然后更新就行了。
PS:数组模拟字典树
Code:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1e6+100;
int bas[35];
const int INF = 2147483645;
struct Trie{
int nxt[MAX<<2][2];
int l[MAX<<2];
int cnt;
int ansl,ansr,ansv;
void init(){
cnt =0;
memset(nxt[0],0,sizeof (nxt[0]));
memset(l,0x3f3f3f3f,sizeof(l));
ansv = 0;
}
int create(){
cnt++;
memset(nxt[cnt],0,sizeof (nxt[cnt]));
return cnt;
}
void insert(int id,int x){
int y = 0;
for (int i=30;i>=0;i--){
int t = x&bas[i];
t>>=i;
if (!nxt[y][t]){
nxt[y][t] = create();
}
y = nxt[y][t];
}
l[y] = min(l[y],id);
}
void query(int id,int x){
int y=0;
int res =0;
for (int i=30;i>=0;i--){
int t = x&bas[i];
t>>=i;
// cout<<t<<" "<<nxt[y][!t]<<endl;
if (nxt[y][!t]){
y =nxt[y][!t];
res+=bas[i];
}else{
y = nxt[y][t];
}
}
// cout<<"Query: "<<l[y]<<" "<<id<<" "<<x<<" "<<res<<" "<<ansv<<endl;
if (res==ansv){
if (l[y]<ansl){
ansl = l[y];
ansr = id;
}
}else if (res>ansv){
ansv = res;
ansl = l[y];
ansr = id;
}
}
void print(int id){
printf("Case #%d:\n%d %d\n",id,ansl+1,ansr);
}
}trie;
void init(){
bas[0] = 1;
for (int i=1;i<=30;i++){
bas[i] = bas[i-1]<<1;
}
}
int main(){
init();
int n,Cas;
scanf("%d",&Cas);
for (int i=1;i<=Cas;i++){
trie.init();
trie.insert(0,0);
scanf("%d",&n);
int sum=0;
for (int j=1;j<=n;j++){
int ai;
scanf("%d",&ai);
sum^=ai;
trie.query(j,sum);
trie.insert(j,sum);
}
trie.print(i);
}
return 0;
}