好题。。。开阔思路
把每个前缀异或和依次插入$01trie$,插之前找一个最优的(就是从高位向低位贪心,尽量走相反方向)看看能不能更新答案,此时相当于找到了区间右端点不超过某个点$r$的最大或和$f[r]$。对于后缀也同理来一波上面的操作,然后就找到了区间左端点端点不少于某个点$l$的最大异或和。所以答案就是$max(f[某个位置]+query(下个位置开始的后缀))$。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<cctype> #include<cstdlib> #include<vector> #include<queue> #include<map> #include<set> #define ll long long #define R register int using namespace std; namespace Fread { static char B[1<<15],*S=B,*D=B; #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++) inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } inline bool isempty(const char& ch) {return ch<=36||ch>=127;} inline void gs(char* s) {register char ch; while(isempty(ch=getchar())); do *s++=ch; while(!isempty(ch=getchar()));} }using Fread::g; using Fread::gs; const int N=400010; int t[N*32][2],tot,n,a[N],s[N],b[N],f[N],ans; inline void ins(int x) { R now=0; for(R i=31;~i;--i) { R ch=(x>>i)&1; if(!t[now][ch]) t[now][ch]=++tot; now=t[now][ch]; } } inline int query(int x) { R now=0,ret=0; for(R i=31;~i;--i) { R ch=(x>>i)&1; if(!t[now][ch^1]) now=t[now][ch^1]; else now=t[now][ch^1],ret+=(1<<i); } return ret; } signed main() { #ifdef JACK freopen("NOIPAK++.in","r",stdin); #endif n=g(); for(R i=1;i<=n;++i) a[i]=g(); for(R i=1;i<=n;++i) s[i]=s[i-1]^a[i]; for(R i=n;i;--i) b[i]=b[i+1]^a[i]; ins(0); for(R i=1;i<=n;++i) f[i]=max(f[i-1],query(s[i])),ins(s[i]); memset(t,0,sizeof(t)); tot=0; ins(0); for(R i=n;i;--i) ans=max(ans,f[i-1]+query(b[i])),ins(b[i]); printf("%d ",ans); }
2019.06.13