BZOJ 4260 Codechef REBXOR

1.题链接

 

求两个不相交的区间各自异或后相加的最大值。n的范围1<=n<=4e5

 

首先我们知道

前 i 个数的异或和前 j 个数的异或相异或: pre[i]^pre[j] = a[i+1]^a[i+2]^……^a[j],(i<j)。异或的后缀和类似。

 

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

 

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

 

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

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a)) 
const int maxn=4e5+10;
int n,m,num[maxn*32],ch[maxn*32][2],tot;
int val[maxn*32],a[maxn],suf[maxn],fi[maxn],dp[maxn];
void init(){
	tot=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[tot][0]=ch[tot][1]=0;
			val[tot]=num[tot]=0;
			ch[u][v]=tot++;
		}
		u=ch[u][v];
		num[u]++;
	}
	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]&&num[ch[u][v^1]]) u=ch[u][v^1];
		else u=ch[u][v];
	}
	return x^val[u];
}
int main(){
	int i,j;
	scanf("%d",&n);
	dp[0]=suf[0]=fi[n+1]=0;
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		suf[i]=suf[i-1]^a[i];
	}
	init();
	for(i=n;i>=1;i--)
		fi[i]=fi[i-1]^a[i];
	int ans=0;
	insert(suf[0]);
	for(i=1;i<=n;i++){
		dp[i]=max(dp[i-1],query(suf[i]));
		insert(suf[i]);
	}
	init();
	insert(fi[n+1]);
	for(i=n;i>=1;i--){
		ans=max(ans,dp[i-1]+query(fi[i]));
		insert(fi[i]);
	}
	printf("%d\n",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值