BZOJ 4260: Codechef REBXOR 【Trie树贪心求区间异或和最值】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88674965

题目传送门

题目分析:

f [ i ] f[i] 表示 [ 1 , i ] [1,i] 中区间异或和的最大值 , g [ i ] g[i] 表示 [ i , n ] [i,n] 中区间异或和的最大值
(一个前缀一个后缀)
那么答案就是 m a x { f [ i ] + g [ i + 1 ] } max\{f[i]+g[i+1]\}
考虑如何求 f f ,记 s [ i ] s[i] 表示 [ 1 , i ] [1,i] 的异或和:
f [ i ] = m a x ( f [ i 1 ] , s [ i ]   x o r   s [ j ] ) , j [ 0 , i 1 ] f[i]=max(f[i-1],s[i]~xor~s[j]),j\in[0,i-1]
要求后者的最值,只需要把前面的每个 s [ j ] s[j] 拆成二进制位插入Trie树中,用 s [ i ] s[i] 进去从高位到低位贪心,看是否有与当前位相反的数存在,存在就加入贡献,往那边走;不存在就往另一边走即可。(贪心是因为 2 n > 2 n 1 + + 2 0 2^n>2^{n-1}+\dots+2^0

最开始插入一个0,这样代码会好写很多。
Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define maxn 400005
using namespace std;
inline void read(int &a){
    char c;while(!isdigit(c=getchar()));
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int n,a[maxn],s[maxn],f[maxn],g[maxn],ans;
int ch[maxn*30][2],tot;
void insert(int x){
    int r=0,v;
    for(int i=30;i>=0;i--,r=ch[r][v]) if(!ch[r][v=(x>>i&1)]) ch[r][v]=++tot;
}
int find(int x){
    int ret=0,r=0,v;
    for(int i=30;i>=0;i--,r=ch[r][v])
        if(ch[r][v=!(x>>i&1)]) ret+=1<<i;
        else if(ch[r][!v]) v=!v;
    return ret;
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    insert(0);
    for(int i=1;i<=n;i++) s[i]=s[i-1]^a[i],f[i]=max(f[i-1],find(s[i])),insert(s[i]);
    memset(ch,0,sizeof ch);
    insert(0);
    for(int i=n;i>=1;i--) s[i]=s[i+1]^a[i],g[i]=max(g[i+1],find(s[i])),insert(s[i]);
    for(int i=1;i<n;i++) ans=max(ans,f[i]+g[i+1]);
    printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/88674965