您的位置:首页 > 其它

可持续化Trie_区间异或最大_bzoj3261

2017-09-04 13:34 447 查看


【bzoj3261】最大异或和


Description

给定一个非负整数序列 {a},初始长度为 N。

有   M个操作,有以下两种操作类型:

1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1。

2 、Q l r x:询问操作,你需要找到一个位置 p,满足 l<=p<=r,使得:

a[p] xor a[p+1] xor … xor a
xor x 最大,输出最大是多少。


Input

第一行包含两个整数 N  ,M,含义如问题描述所示。

第二行包含 N个非负整数,表示初始的序列 A 。

接下来 M行,每行描述一个操作,格式如题面所述。


Output

假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。


Sample Input

5 5

2 6 4 3 6

A 1

Q 3 5 4

A 4

Q 5 7 0

Q 3 6 6

对于测试点 1-2,N,M<=5 。

对于测试点 3-7,N,M<=80000 。

对于测试点 8-10,N,M<=300000 。

其中测试点 1, 3, 5, 7, 9保证没有修改操作。

对于 100% 的数据, 0<=a[i]<=10^7。


Sample Output

4

5

6


HINT

对于      100%  的数据,     0<=a[i]<=10^7  。

对于求区间异或的最大值这种问题,我们首先可以这样思考,我们要知道这个区间当中哪一段开始是最大的,那么我们需要维护一下他的前缀异或和。比如题目中给定的a[1]...a
我们用b[i]表示a[1]^...a^[i] 那么我们用b[i-1]^b
就可以得到a[i]^...^a
的值了, 然后再异或上题目中给定的x。到这里我们分析可以得到我们需要计算的其实是b[i-1]^b
^x的最大值,因为b
^x的值是确定的,那么我们只需要计算b[i-1]中i-1取哪一个的时候可以与b
^x异或的值最大即可。而我们又知道i是从题目中给定的l到r的范围内确定的,因此i-1属于[l-1,r-1],这样我们就确定好了区间的范围了。然后我们怎么维护每一个b[i]呢?
可以考虑可持久化的数据结构,这里用trie树因为存二进制位,讲到可持久化数据结构,应该有学过主席树,没有的话可以看我的上一篇,可持久化
a2e7
的思想就是保存每一个修改/更新的版本,只在每一个版本上进行需要更新的节点。 好了,看代码,我用的是非指针的,并且不是递归,感觉这样快点,虽然代码变长了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int a[maxn],b[maxn];
struct Node{
int sum;
int l;
int r;
Node(){
l=0,r=0,sum=0;
}
}node[maxn<<5];
int root[maxn],cnt,K=5,n,m;//K表示該題的数据范围的对应二进制的位数
char ch[10];
int update(int last,int val){
int now = ++cnt;//cnt表示的是该节点的标号
node[now] = node[last];//一开始的的时候新版本的根节点指向上一个版本的根节点
int temp = now;
for(int i=K;i>=0;i--){
int bit = (val>>i)&1;
if(bit){
node[temp].l = node[last].l;//左边的不去修改,一直复制上一个版本的信息即可
node[temp].r = ++cnt;//当前版本中该位置需要有更新所以新建节点
temp = node[temp].r, last = node[last].r;
node[temp].sum = node[last].sum+1;//不能是自己++ 需要保持与上一个版本的信息一致 所以在上一个版本基础上++
}
else{//该位是0 往左走
node[temp].r = node[last].r;
node[temp].l = ++cnt;
temp = node[temp].l, last = node[last].l;
node[temp].sum = node[last].sum+1;
}
}
return now;//返回当前版本的根节点的标号
}
int query(int pre, int now,int val){
int ans = 0;
for(int i=K;i>=0;i--){
int bit = (val>>i)&1;
if(bit){//当前位置是1 则需要异或0 可以得到max 所以往左走
if(node[now].l==0){//左节点没有 往又走
now = node[now].r;
pre = node[pre].r;
continue;
}
int count = node[node[now].l].sum - node[node[pre].l].sum;//当前版本与之前版本的个数差
if(count>0){
ans += (1<<i);
now = node[now].l;
pre = node[pre].l;
}
else{
now = node[now].r;
pre = node[pre].r;
}
}
else{//当前位是0 需要异或1得到最大
if(node[now].r==0){//右节点没有 往左走
now = node[now].l;
pre = node[pre].l;
continue;
}
int count = node[node[now].r].sum - node[node[pre].r].sum;//当前版本与之前版本的个数差
if(count>0){
ans += (1<<i);
now = node[now].r;
pre = node[pre].r;
}
else{
now = node[now].l;
pre = node[pre].l;
}
}
}
return ans;
}
void init(){
cnt = 0;//每一次都需要重置0的个数
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(root,0,sizeof(root));
}
int main(){
while(~scanf("%d%d",&n,&m)){
// n++;
// root[1] = update(root[0],0);//一开始要插入为0 因为最初异或和是0
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i] = b[i-1]^a[i];
root[i] = update(root[i-1],b[i]);
// printf("%d\n",root[i]);
}
for(int i=1;i<=m;i++){
scanf("%s",ch);
if(ch[0] == 'A'){
n+=1;
scanf("%d",&a
);
b
= b[n-1]^a
;
root
= update(root[n-1],b
);
}
else{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);//因为题目中需要求a[i]^..a
^x 最大
//我们将b
作为记录a[1]^..a
的前缀和 所以要求该式子 我们只需要求 b[i-1]^b
^x 最大即可
//所以我们要定位i-1 且 l<=i<=r 所以 l-1<=i-1<=r-1 因此在下面的区间搜寻即可
int ans = query(root[l-2],root[r-1],x^b
);
printf("%d\n",ans);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: