[BZOJ4260] Codechef REBXOR

传送门:>Here<

题意:给出一个长度为N的序列,求$Max\{\ (a[l_1]⊕...⊕a[r_1])\ +\ (a[l_2]⊕...⊕a[r_2]) \}$ ($1 \leq l_1 \leq r_1 < l_2 \leq r_2 \leq N$)  ($N \leq 4*10^5$)

解题思路

  首先理解题目的意思——求解两段区间,使这两段区间的异或和的和最大。

  我们可以考虑维护两个数组$L$和$R$,其中$L[i]$表示在区间$[1, i]$中选出一段区间的最大异或和。$R[i]$则表示区间$[i, N]$。因此答案一定是$Max\{ L[i] + R[i+1] \}$。因此现在只需要考虑如何求出这两个数组就行了

  因为$L$和$R$的求法是类似的,所以我们就只谈$L$的求法。

  考虑维护一个异或前缀和数组$x$,$x[i]$表示$a[1] ⊕ a[2] ⊕ ... ⊕ a[i-1] ⊕ a[i]$。

  我们有以下几条性质:

    异或是满足交换律的                      (1)

    相同的数异或为0                           (2)

    0异或一个数字得到那个数字本身  (3)

  因此我们就可以得到前缀和数组$x[i] ⊕ x[j]$就表示区间$[j, i]$的异或和。因为$x[i]  ⊕  x[j] = a[1] ⊕ ... ⊕ a[i] ⊕ a[1] ⊕ ... ⊕ a[j]$,把相同的放在一起抵消了,剩下的就是区间$[j, i]$的异或和

  因此要得到区间$[1, i]$的最大异或子段和,就等同于求与$x[i]$异或和最大的$x[j]$了,这篇文章中我们已经详细阐述了这个问题的求法。因此问题就解决了

  考虑$L[i]$怎么转移?$L[i] = Max\{ L[i-1], x[i], x[i] ⊕ x[j] \}$。其中$L[i-1]$表示当前数$a[i]$不参与到区间内(我就是忘记考虑这种情况了,两个区间不一定只相距1啊!!1),$x[i]$表示整个区间都参与,不需要再找最大的$x[j]$了。

Code

  做L和R时要分开,数组要清零

/*By DennyQi*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 400010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w;
}
int N;
int a[MAXN],x[MAXN],L[MAXN],R[MAXN];
int ch[MAXN<<5][2],End[MAXN<<5],num_node;
bool b[40];
inline void Convert(int x){
    memset(b,0,sizeof(b));
    for(int i = 33; x > 0; --i){
        b[i] = x%2;
        x >>= 1;
    }
}
inline void Insert(int x){
    Convert(x);
    int u=0;
    for(int i = 1; i <= 33; ++i){
        if(!ch[u][b[i]]) ch[u][b[i]] = ++num_node;
        u = ch[u][b[i]];
    }
    End[u] = x;
}
inline int Query(int x){
    int u=0;
    Convert(x);
    for(int i = 1; i <= 33; ++i){
        if(!ch[u][!b[i]]) u = ch[u][b[i]];
        else u = ch[u][!b[i]];
    }
    return End[u];
}
inline void Clear(){
    memset(End,0,sizeof(End));
    memset(ch,0,sizeof(ch));
    num_node = 0;
}
int main(){
    N=r;
    for(int i = 1; i <= N; ++i){
        a[i]=r;
        x[i] = x[i-1] ^ a[i];
    }
    L[1] = x[1];
    Insert(x[1]);
    for(int i = 2; i <= N; ++i){
        L[i] = Max(x[i], Max(L[i-1], Query(x[i]) ^ x[i]));
        Insert(x[i]);
    }
    Clear();
    for(int i = N; i >= 1; --i) x[i] = x[i+1] ^ a[i];
    R[N] = x[N];
    Insert(x[N]);
    for(int i = N-1; i >= 1; --i){
        R[i] = Max(x[i], Max(R[i+1], Query(x[i]) ^ x[i]));
        Insert(x[i]);
    }
    int ans = -1;
    for(int i = 1; i < N; ++i) ans = Max(ans, L[i]+R[i+1]);
    printf("%d", ans);
    return 0;
}

 

posted @ 2018-08-05 16:06  DennyQi  阅读(169)  评论(0编辑  收藏  举报