bzoj 4260 Codechef REBXOR



4260: Codechef REBXOR

时间限制: 10 Sec  内存限制:256 MB
提交: 535  解决: 243
[
提交 ][ ][ ]

题目描述

输入

输入数据的第一行包含一个整数N,表示数组中的元素个数。
第二行包含N个整数A1,A2,…,AN。

输出

输出一行包含给定表达式可能的最大值。

样例输入

5
1 2 3 1 2

样例输出

6

提示

满足条件的(l1,r1,l2,r2)有:(1,2,3,3),(1,2,4,5),(3,3,4,5)。

对于100%的数据,2 ≤ N ≤ 4*105,0 ≤ Ai ≤ 109。
题意:找出两个不相交区间异或和的最大和值。
题解:
看到区间异或,首先这题应该是用01字典树去写。我们求一个前缀异或和,求一个后缀异或和。维护一个dp数组,dp[i]表示以i为结尾的最大区间异或值。再通过后缀数组维护max。具体解释在代码中。
代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define mem(a) memset(a, 0, sizeof(a))
#define eps 1e-5
#define MAX 400030
#define INF 0x3f3f3f3f
using namespace std;
int pre[MAX];
int suf[MAX];
int a[MAX];
int dp[MAX];
int ch[32*MAX][2];
int val[32*MAX];
int sz;
void init()
{
    sz=1;
    mem(ch[0]);
}
void Insert(int a)
{
    int u=0;
    for(int i=31;i>=0;i--)
    {
        int c=((a>>i)&1);
        if(!ch[u][c])
         {
             mem(ch[sz]);
             val[sz]=0;
             ch[u][c]=sz++;
         }
         u=ch[u][c];
    }
    val[u]=a;

}
int query(int a)
{
    int u=0;
    for(int i=31;i>=0;i--)
    {
        int c=((a>>i)&1);
        if(ch[u][c^1])
            u=ch[u][c^1];
        else
            u=ch[u][c];
    }
    return val[u]^a;

}//以上为模板
int main()
{
    int n;
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    pre[0]=suf[n+1]=0;
    for(int i=1;i<=n;i++)
        pre[i]=pre[i-1]^a[i];
    for(int i=n;i>0;i--)
        suf[i]=suf[i+1]^a[i];
    mem(dp);
    Insert(0);
    for(int i=1;i<=n;i++)
    {
        dp[i]=max(dp[i-1],query(pre[i]));
        Insert(pre[i]);//这里刚开始难理解,为什么加进去的是前缀和,得到的却是连续区间异或和。因为你加入的前缀和,会跟之前加进去的前缀和进行异或操作求得最大值,而跟前面的进行异或操作,就使得一个数跟自己异或。
    }
    init();
    int maxn=0;
    Insert(0);
    for(int i=n;i>0;i--)
    {
        maxn=max(maxn,query(suf[i])+dp[i-1]);
        Insert(suf[i]);
    }
    printf("%d\n",maxn);
}




你可能感兴趣的:(字典树)