BZOJ 4260: Codechef REBXOR (01字典树+dp)

传送门:BZOJ 4260



题目大意:

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



前置技能: 01字典树。



思路:

首先看下异或的性质,关于异或我们知道: 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 个数的后部分区间就可以了。



代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#define MAXN 400010
using namespace std;

int tol;
int val[MAXN*32];
int ch[MAXN*32][2];
//dp[i]表示前 i个数中任意区间异或的最大值 
//pre[i]表示前 i个数的异或结果,即前缀和 
//suf[i]表示第 i个数之后的互素异或结果,即后缀和 
int dp[MAXN],pre[MAXN],suf[MAXN];

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

void insert(int x)
{
	int u=0;
	for(int i=32;i>=0;i--)
	{
		int v=(x>>i)&1;
		if(!ch[u][v])
		{
			ch[tol][0]=ch[tol][1]=0;
			val[tol]=0;
			ch[u][v]=tol++;
		}
		u=ch[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(ch[u][v^1]) u=ch[u][v^1];
		else u=ch[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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值