奶牛异或
时间限制: 2 Sec 内存限制: 64 MB
提交: 265 解决: 41
[提交][状态][讨论版]
题目描述
农民约翰在喂奶牛的时候被另一个问题卡住了。他的所有N(1 <= N <= 100,000)个奶牛在他面前排成一行(按序号1..N的顺序),按照它们的社会等级排序。奶牛#1由最高的社会等级,奶牛#N最低。每个奶牛同时被赋予了一个唯一的数在0..2^21 - 1的范围内。帮助农民约翰找出应该从那一头奶牛开始喂,使得从它开始的某一个连续的自序列上的奶牛的数的异或最大。如果有多个这样的子序列,选择结尾的奶牛社会等级最高的。如果还不唯一,选择最短的。
输入
• 第1行:一个单独的整数N。 • 第2到N + 1行:N个0..2^21 – 1之间的整数,代表每头奶牛的被赋予的数。第j行描述了社会等级j – 1的奶牛。
输出
• 第 1 行: 3个空格隔开的整数,分别为:最大的异或值,序列的起始位置、终止位置。
样例输入
5
1
0
5
4
2
样例输出
6 4 5
提示
来源
此题发现同一个值异或两遍就是0,所以用前缀f[i]记录从1到i的异或和,x到y异或和就是f[y]xorf[x−1]
要找最大值就是对每个f[i]找1到i−1中与它异或最大的
因为异或是一样为0,不一样为1,就可以用字典树来记录和查找
#include<iostream>
#include<cstdio>
using namespace std;
int tree[9000000];
int f[200000];
void build(int x,int y,int z,int p){
//cout<<p<<" "<<z<<endl;
if (z==-1){
tree[p]=y;
return;
}
int yh=z;
if ((x&(1<<yh))!=0) build(x,y,yh-1,p+p+1);
else build(x,y,yh-1,p+p);
if (tree[p+p]!=0||tree[p+p+1]!=0)tree[p]=1;
}
int search(int x,int y,int z,int p){
if (z==-1)
return tree[p];
int yh=z;
if (((x&(1<<yh))!=0&&tree[p+p]>0)||(tree[p+p+1]==0))
return search(x,y,yh-1,p+p);
else
if (((x&(1<<yh))==0&&tree[p+p]>0)||(tree[p+p]==0))
return search(x,y,yh-1,p+p+1);
}
int main(){
int n;
scanf("%d",&n);
f[0]=0;
int ma=0;
for (int i=1;i<=n;++i){
int t;
scanf("%d",&t);
f[i]=f[i-1]^t;
int a=0;
int j=f[i];
while (j!=0) j/=2,++a;
ma=max(ma,a);
//cout<<f[i]<<endl;
}
// for (int i=1;i<=n;++i) build(f[i],i,ma-1,1);
//cout<<tree[7]<<" "<<tree[8]<<" "<<tree[9]<<" "<<tree[10]<<" "<<tree[11]<<endl;
int ans=-1<<29,numi=0,numj=0;
for (int i=1;i<=n;++i){
int j=search(f[i],i,ma-1,1)+1;
int zhi=f[i]^f[j-1];
if ((zhi>ans)||(zhi==ans&&i<numj)||(zhi==ans&&i==numj&&i-j+1<numj-numi+1)){
ans=zhi;
numi=j;
numj=i;
}
build(f[i],i,ma-1,1);
}
printf("%d %d %d\n",ans,numi,numj);
return 0;
}