BZOJ 4260 Codechef REBXOR(01字典树)

题目大意:

给你 n 个数,让你求两个不相交的区间元素异或后的和的最大值。本题中 n 的上限是  4*10^5.

首先看下异或的性质,关于异或我们知道: 0^a=a ,  a^a=0 。前 i 个数的异或和前 j 个数的异或相异或: pre[i]^pre[j] = a[i+1]^a[i+2]^……^a[j],(i<j)。异或的后缀和类似。

于是我们可以先求出异或的前缀 pre[i]和后缀和 suf[i]。dp[i]表示前 i个数中任意区间异或的最大值,可以依次求与 pre[i] 相异或结果的最大值,然后把 pre[i] 插入到 01字典树中。

这样对于每个 pre[i] 他会和之前的 i-1 个异或前缀和的共有部分所抵消,也就相当于是求任意区间的异或结果的最大值了。这样求出了一个区间,同理可利用后缀和求出另一个区间。

怎么保证两个区间不相交呢?可以通过使前后两个区间一个为不包含第 i个数的前部分区间,一个是包含第 i 个数的后部分区间就可以了。

AC代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<queue>
#include<map>
#include<iomanip>
#include<math.h>
#include<sstream>
using namespace std;
typedef long long ll;
typedef double ld;
const int MAXN = 400010;
int tol;
int val[MAXN*32];
int tree[MAXN*32][2];
//dp[i]表示前 i个数中任意区间异或的最大值
//pre[i]表示前 i个数的异或结果,即前缀和
//suf[i]表示第 i个数之后的互素异或结果,即后缀和
int dp[MAXN],pre[MAXN],suf[MAXN];

void init()
{
    //初始化
    tol=1;
    tree[0][0]=tree[0][1]=0;
}

void insert(int x)
{
    int u=0;
    for(int i=32; i>=0; i--)
    {
        int v=(x>>i)&1;
        if(!tree[u][v])
        {
            tree[tol][0]=tree[tol][1]=0;
            val[tol]=0;
            tree[u][v]=tol++;
        }
        u=tree[u][v];
    }
    val[u]=x;
}

int query(int x)
{
    int u=0;
    for(int i=32; i>=0; i--)
    {
        int v=(x>>i)&1;
        if(tree[u][v^1])
            u=tree[u][v^1];
        else
            u=tree[u][v];
    }
    return x^val[u];
}

int main()
{
    int i,j,n,ans,a[MAXN];
    while(~scanf("%d",&n))
    {
        for(i=1; i<=n; i++)
            scanf("%d",&a[i]);
        pre[0]=suf[n+1]=0;
        for(i=1; i<=n; i++)
            pre[i]=pre[i-1]^a[i]; //前缀和
        for(i=n; i>=1; i--)
            suf[i]=suf[i+1]^a[i]; //后缀和
        memset(dp,0,sizeof(dp));
        init(); //第一次初始化
        insert(pre[0]);
        for(i=1; i<=n; i++)
        {
            //求出 dp[i],即前 i个数的任意区间异或的最大值
            dp[i]=max(dp[i-1],query(pre[i]));
            insert(pre[i]);
        }
        init(); //第二次初始化
        ans=0;
        insert(suf[n+1]);
        for(i=n; i>=1; i--)
        {
            //求出最终结果
            //query(suf[i])+dp[i-1]即 i 之后的任意区间异或值和 i之前的任意区间异或值之和
            ans=max(ans,query(suf[i])+dp[i-1]); //dp[i-1] 保证了两个区间不相交
            insert(suf[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值