题意简介
题目就是叫你找两个不重合的非空区间,使得这两个区间里的数异或后相加的和最大
(看到异或,没错就决定是你了可持久化trie!)
思路
水一波字典树,莫名觉得这题可持久化能过,于是水了一发挂了,造了一波数据,然后发现是自己在做完一遍可持久化之后cnt 没有清零....
其实要用可持久化trie 来做的话也就是常规操作(话说普通字典树不也是常规操作?)
也就是前缀和往可持久化trie 上update , 然后每个 L[i]、R[i] 记录当前点为右(左)区间的最大区间异或和
然后就是枚举断点了,考虑我们枚举到的断点前的那个区间其实是确定的(异或和最大的那个),
那么我们拿当前断点作为第二个 区间的左端点,前面的区间由 lef 变量不断更新,最后就能累加出答案。
于是没什么好说的了,板子题。
代码如下
1 //by Judge 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 const int M=4e5+111; 6 //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 inline int read(){ 9 int x=0,f=1; char c=getchar(); 10 for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; 11 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; 12 } 13 int n,cnt,a[M],L[M],R[M]; 14 int d[35],rt[M<<2],son[M<<5][2],sum[M<<5]; 15 inline void split(int k){ //换二进制 16 int i,len=0; 17 while(k) d[++len]=k&1,k>>=1; 18 for(int i=len+1;i<=31;++i) d[i]=0; 19 } 20 inline void update(int& now,int las){ //可持久化的更新 21 sum[now=++cnt]=sum[las]+1; 22 int i,tmp=now; 23 for(i=31;i;--i){ 24 son[tmp][d[i]^1]=son[las][d[i]^1], 25 son[tmp][d[i]]=++cnt,las=son[las][d[i]], 26 sum[tmp=cnt]=sum[las]+1; 27 } 28 } 29 inline int query(int u,int v){ //询问区间内与当前数的最大异或和 30 int ans=0,i; 31 for(i=31;i;--i){ 32 if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]]>0) 33 ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1]; 34 else u=son[u][d[i]],v=son[v][d[i]]; 35 } return ans; 36 } 37 int main(){ //分函数都是常规操作(因为我都是直接搞了自己的板子) 38 int x,lef,res=0; 39 n=read(),++n; 40 split(0),update(rt[1],rt[0]); 41 for(int i=2;i<=n;++i) 42 a[i]=read(); 43 for(int i=2,sum=0;i<=n;++i){ 44 split(sum^=a[i]), 45 update(rt[i],rt[i-1]), 46 L[i]=query(rt[0],rt[i]); 47 } 48 cnt=0,split(0),update(rt[1],rt[0]); //清零,从后往前再来一遍 49 for(int i=n,sum=0;i>=2;--i){ 50 split(sum^=a[i]); 51 update(rt[n-i+2],rt[n-i+1]), 52 R[i]=query(rt[0],rt[n-i+2]); 53 } lef=L[2]; 54 for(int i=3;i<=n;++i){ //从左到右处理答案 55 res=max(res,lef+R[i]), 56 lef=max(lef,L[i]); 57 } printf("%d ",res); return 0; 58 }