1. 程式人生 > >bzoj-4260 Codechef REBXOR

bzoj-4260 Codechef REBXOR

題目連結

題目大意
給出一個序列求MAX{ (a[L1] xor a[L1+1] xor … xor a[R1])+(a[L2] xor a[L2+1] xor…xor a[R2]) }

其中,1≤L1≤R1 < L2≤R2≤N 。

資料範圍:2≤N≤4*10^ 5,0≤a[i]≤10^9 。

題解
這題比較裸吧。(本文所有字首和預設理解為異或綴和)。

異或是可以利用字首和來弄的,那麼我們肯定毫不猶豫地做一趟字首和。

兩個區間是相似的,如果我們能求出一個區間,那麼另一個區間是同理的,只需規定一下範圍就好了。

接著思考如何求一個區間,即對於 s[i] ^ s[j],s[j] 取誰的時候最優?

顯然,從高位開始,儘可能和 s[i] 錯開。例如
(二進位制下)
s[i]=11001 那麼 s[j]=01001 時顯然比 s[j]=10110 時好。
如果能找到一個 s[j] 讓高位同 s[i] 錯開,那麼即使後面全部對上,也比高位對上的來的優秀。

懷著這樣的想法,我們就需要這麼一個能夠按位查詢是否存在這樣一個元素的資料結構,顯然 Trie 樹可以勝任。

由此我們可以構造出一個數組 L[i] 表示 L1 位置不知道,R1 位置為 i 的最大區間異或和。
經過修正可以用來表示 L1 位置不知道,R1 的位置不大於 i 的最大區間異或和。
同理可以求出一個類似的 R[i],那麼 ans=MAX{ L[i]+R[i+1] }

程式碼
這題寫到我抓狂,為什麼指標那麼慢?
原版跑 9 s 多,拼死優到 6.8s 再優不了了。

指標版:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=4e5+5,maxp=15000000;
int n,a[maxn],e[35],len=32,T,L[maxn],R[maxn];
#define LL long long
LL ans;
struct js{
    js*
son[2]; js(){son[0]=son[1]=NULL;} void cl(){son[0]=son[1]=NULL;} }*top,ne_[maxp+1]; int rad() { int ret=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar(); return ret*f; } int nxt(){return(++T)>=maxp?(T=0):T;} void set_()//Trie樹的插入 { if (top==NULL) {top=&ne_[nxt()];top->cl();} js*x=top; for (int i,stp=len;stp;--stp) { i=e[stp]; if (x->son[i]==NULL) {x->son[i]=&ne_[nxt()];x->son[i]->cl();} x=x->son[i]; } } void get_(int&ans)//求L[i]的第一個定義。 { js*x=top; for (int i,stp=len;x!=NULL&&stp;--stp) { i=e[stp]; if (x->son[i^1]!=NULL) { ans|=1<<stp-1; x=x->son[i^1]; }else { if (ans&1<<stp-1) ans^=1<<stp-1;; x=x->son[i]; } } } void ready(int x)//拆數,拆成“字串”,用於Trie數 { for (int i=1;i<=32;++i) e[i]=x&1,x>>=1; } int main() { // freopen("OL.in","r",stdin);freopen("OL.out","w",stdout); n=rad(); top=NULL; ready(0); set_(); for (int i=1,XOR=0,sum;i<=n;++i) { XOR^=a[i]=rad(); ready(XOR); sum=XOR; get_(sum); L[i]=max(L[i-1],sum); set_(); } top=NULL; ready(0); set_(); for (int i=n,XOR=0,sum;i>=1;--i) { XOR^=a[i]; ready(XOR); sum=XOR; get_(sum); R[i]=max(R[i+1],sum); set_(); } ans=0; for (int i=1;i<n;++i) ans=max(ans,(LL)L[i]+R[i+1]); printf("%lld\n",ans); return 0; }

陣列版也差不多慢,6.8 左右。
陣列版:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=4e5+5,maxp=15000000,len=32;
int n,a[maxn],e[35],T,L[maxn],R[maxn];
#define LL long long
LL ans;
struct js{
    int son[2];
    js(){son[0]=son[1]=0;}
    void cl(){son[0]=son[1]=0;}
}t[maxp+1];
int rad()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
int nxt(){return(++T)>=maxp?(T=0):T;}
void set_()
{
    int x=1;
    for (int i,stp=len;stp;--stp)
    {
        i=e[stp];
        if (t[x].son[i]==0) {t[x].son[i]=nxt();t[t[x].son[i]].cl();}
        x=t[x].son[i];
    }
}
int get_()
{
	int x=1,ret=0;
    for (int i,stp=len;x!=0&&stp;--stp)
    {
        i=e[stp];
        if (t[x].son[i^1]!=0)
        {
            ret|=1<<stp-1;
            x=t[x].son[i^1];
        }else x=t[x].son[i];
    }
    return ret;
}
void ready(int x){for (int i=1;i<=32;++i) e[i]=x&1,x>>=1;}
int main()
{
//	freopen("OL.in","r",stdin);freopen("OL.out","w",stdout);
    t[1].cl();ready(0);set_();n=rad();
    for (int i=1,XOR=0;i<=n;++i)
    {
        ready(XOR^=a[i]=rad());
        L[i]=max(L[i-1],get_());
        set_();
    }
    t[1].cl();ready(0);set_();
    for (int i=n,XOR=0;i>=1;--i)
    {
        ready(XOR^=a[i]);
        R[i]=max(R[i+1],get_());
        ans=max(ans,(LL)L[i]+R[i+1]);
        set_();
    }
    printf("%lld\n",ans);
    return 0;
}