[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; }