bzoj4260: Codechef REBXOR(01Trie)
題目傳送門
。
解法:
01Trie。
聽名字大概就知道怎麼回事了。
自己yy了下也不知道對不對。。
問題大概就是:
很多個數,求他們異或某一個數的最大值。
我的建法是這樣的:
每個數轉化成二進位制的01序列。
從最高位開始建01trie。跟普通trie差不多。只是一個點只有兩個兒子的區別。
這裡的最高位不是指數本身的最高位。而是最大數的最高位。
那麼此題中範圍為int。我把最高位設成了30位。
那麼怎麼求最大值呢。
因為異或是同為0不同為1。
那麼每到一個位的時候就去問問相反的兒子有沒有。
有的話這個位就為1,否則為0。
舉個例子:
如果當前位為1。那麼我們應該去找當前Trie上的位置有沒有0。
有0的話那麼這個位置異或後就為1,否則為0。
這樣我們可以貪心出最大值。
對於此題:
異或和是滿足加法的性質。
比如:sum[i]表示a[1]^a[2]^…a[i]。
那麼sum[i]^sum[j]=a[j+1]^a[j+1]….a[i]。
這樣我們就可以求出從每個點往左/右最大異或和。
然後O(n)求解就行了
程式碼實現:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct node {int son[2];node(){son[0]=son[1]=-1;}}tr[15100000];int tot,root,a[410000],s[31];
void build() {
int x=root;
for(int i=30;i>=1;i--) {
if(tr[x].son[s[i]]==-1) {tot++;tr[x].son[s[i]]=tot;}
x=tr[x].son[s[i]];
}
}
void get(int x) {
int len=0;
while(x!=0) {s[++len]=x%2;x/=2;}
for(int i=len+1;i<=30;i++)s[i]=0;
}
int L[410000],R[410000];
int main() {
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int sum=0;tot=root=0;
for(int i=1;i<=n;i++) {
sum^=a[i];get(sum);build();int x=root;
int t=0;
for(int j=30;j>=1;j--) {
if(tr[x].son[1-s[j]]!=-1) {x=tr[x].son[1-s[j]];t=t*2+1;}
else {x=tr[x].son[s[j]];t=t*2;}
}L[i]=max(sum,t);
}
for(int i=0;i<=tot;i++)tr[i].son[1]=tr[i].son[0]=-1;
root=tot=0;
sum=0;
for(int i=n;i>=1;i--) {
sum^=a[i];get(sum);build();int x=root;
int t=0;
for(int j=30;j>=1;j--) {
if(tr[x].son[1-s[j]]!=-1) {x=tr[x].son[1-s[j]];t=t*2+1;}
else {x=tr[x].son[s[j]];t=t*2;}
}R[i]=max(sum,t);
}
int l=L[1],ans=0;
for(int i=2;i<=n;i++) {
ans=max(ans,l+R[i]);l=max(l,L[i]);
}printf("%d\n",ans);
return 0;
}