1. 程式人生 > >BZOJ 4260 Codechef REBXOR (區間異或和最值)【01字典樹】

BZOJ 4260 Codechef REBXOR (區間異或和最值)【01字典樹】

兩個 font pri ech c++ def print char ret

<題目鏈接>

題目大意:
給定一個序列,現在求出兩段不相交的區間異或和的最大值。

解題分析:

區間異或問題首先想到01字典樹。利用前綴、後綴建樹,並且利用異或的性質,相同的兩個數異或變成0,從而將前綴操作轉化為區間操作,比如:$(a_1 \oplus a_2)\oplus(a_1 \oplus a_2 \oplus a_3 \oplus a_4) = a_3 \oplus a_4$。然後利用簡單的$dp$,$predp[i]$記錄前$[1~i]$ 任意區間的區間異或最大值(註意不是前綴),$nxtdp$同理記錄後綴區間的最大值。

本題空間卡得比較緊,所以沒有另外用一個$val[now]$數組記錄Trie樹前綴節點所表示的值,而是在$query$中用$ans$代替。

#include <bits/stdc++.h>
using namespace std;

const int N = 4e5+5;
typedef long long ll;
ll n,pos=1,arr[N],nxt[N*30][2],predp[N],nxtdp[N];

template<typename T>
inline T read(T&x) {     
    x = 0;int f = 1; char ch = getchar();
    while(ch<0 || ch>9) { if(ch == 
-) f=-1; ch=getchar(); } while(ch>=0 && ch<=9) { x=x*10+ch-0; ch=getchar(); } return x*f; } void Insert(ll x){ int now=1; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(!nxt[now][to])nxt[now][to]=++pos; now=nxt[now][to]; } } ll query(ll x){
int now=1;ll ans=0; for(int i=30;i>=0;i--){ int to=(x>>i)&1; if(nxt[now][to^1])now=nxt[now][to^1],ans+=(1<<i); //因為最終異或得到的答案就是x中所不包含的數位 else now=nxt[now][to]; } return ans; //return x^val[now]; 因為本題空間卡得緊,所以就不能寫成這種形式 } int main(){ read(n); for(int i=1;i<=n;i++)read(arr[i]); for(int cur=0,i=1;i<=n;i++){ Insert(cur^=arr[i]); predp[i]=max(query(cur),predp[i-1]); //因為一個數被異或兩次等於0,所以這裏利用Trie樹的前綴操作來實現區間操作 }//得到 i 的前綴區間異或和的最大值 memset(nxt,0,sizeof(nxt)); for(int cur=0,i=n;i>=1;i--){ Insert(cur^=arr[i]); nxtdp[i]=max(query(cur),nxtdp[i+1]); }//得到 i 的後綴異或區間和的最大值 ll ans=-1; for(int i=1;i<=n;i++)ans=max(ans,predp[i]+nxtdp[i]); printf("%lld\n",ans); }

2019-03-02

BZOJ 4260 Codechef REBXOR (區間異或和最值)【01字典樹】