#LOJ 10051 Nikitosh 和异或

#10051. 「一本通 2.3 例 3」Nikitosh 和异或

题意就是让着选两个区间,区间不能交叉,令他们的区间异或的和最大,怎么做?

这篇题解(Link)里有区间两个数异或最大值,想办法让这个区间异或值成两个点的区间异或,很显然,前缀异或。

异或也有前缀性。比如\(a \oplus b = c\),则 \(c \oplus b=a\)\(yh[l -r]=yh[l-1] \oplus yh[r]\)

因为两区间不交叉,所以:
从前做一遍前缀异或,求出到 \(i\) 位置的最大异或值
从后做一遍后缀异或,求出到 \(i\) 位置的最大异或值

最后枚举分割点相加即可。

记得把零先插进去,因为说不定一段区间的前缀异或就是最大的而不是和到一个 \(l\) 的异或最大。

/*
Knowledge : Rubbish Algorithm
Work by :Gym_nastics
Time : O(AC)
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int N=2e6+6;

int read() {
    int x=0,f=0;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
    return f?-x:x;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+48);
}

int Trie[N<<2][2],en[N<<2],tot; 

void Insert(int x){
    int pos=0;
    for(int i=31;i>=0;i--){
        int ch=((x>>i)&1);
        if(!Trie[pos][ch]) Trie[pos][ch]=++tot;
        pos=Trie[pos][ch];
    }
    en[pos]=true;
}

int search(int x){
    int pos=0,res=0;
    for(int i=31;i>=0;i--){
        int ch=(((x>>i)&1)^1);
        if(Trie[pos][ch]) res=res<<1|1,pos=Trie[pos][ch];
        else res<<=1,pos=Trie[pos][ch^1];
    } return res;
}

int a[N],Lmax[N],Rmax[N];

signed main() {
   int n=read();int yh=0;Insert(0);
   for(int i=1;i<=n;i++){
       a[i]=read();yh^=a[i];Insert(yh);
       Lmax[i]=max(Lmax[i-1],search(yh));
   }memset(Trie,0,sizeof Trie);yh=0;
   memset(en,0,sizeof en);tot=0;
   for(int i=n;i>=1;i--){
       yh^=a[i];Insert(yh);
       Rmax[i]=max(Rmax[i+1],search(yh));
   }int Ans=0;for(int i=1;i<n;i++)
   Ans=max(Ans,Lmax[i]+Rmax[i+1]);
   return print(Ans),0;
}

posted @ 2022-03-13 21:16  Gym_nastics  阅读(44)  评论(0编辑  收藏  举报