bzoj4260: Codechef REBXOR(01Trie)

Hanks_o發表於2018-04-22

題目傳送門

解法:
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;
}