Description
给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。
Input
第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。
Output
假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。
对于测试点 1-2,N,M<=5 。
对于测试点 3-7,N,M<=80000 。
对于测试点 8-10,N,M<=300000 。
其中测试点 1, 3, 5, 7, 9保证没有修改操作。
0<=a[i]<=10^7。
题目分析
先计算出数列的异或前缀和
a[N]^x是定值
所以问题所求就是在前缀和的l到r区间内找到一个数使其与a[N]^x异或值最大
经典的可持久化化Trie
建立N个01trie
第i个trie插入得是前缀和数列中前1~i个数
同时在每个节点维护一个sum记录该节点被插入过几次
建树方式和主席树几乎无差
学过主席树的同学应该一眼就能看懂
对于l~r区间同主席树思路一样
就是用第r棵与第l-1棵相减
查询得话每次走与当前数值相反的路就好
需要注意的是因为插入的是前缀和
每次查询区间要左移一位
或者像我这样把原数列全部后移一位,并将1建为空树
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=1e7;
int n,m;
int xr,tot;
int trie[maxn<<1],sum[maxn<<1],nxt[maxn<<1][2];
int update(int pre,int x,int cnt)
{
int rt=++tot; sum[rt] = sum[pre] + 1;
if(cnt<0) return rt;
int d=x>>cnt&1;//二进制下当前位
nxt[rt][d^1]=nxt[pre][d^1];//不用修改,指针指回上一棵trie
nxt[rt][d]=update(nxt[pre][d],x,cnt-1);//要新建的结点
return rt;
}
int query(int u,int v,int x,int cnt)
{
if(cnt<0) return 0;
int d=x>>cnt&1;
int ss=sum[nxt[v][d^1]]-sum[nxt[u][d^1]];//先判断相反位是否可以走
if(ss>0) return (1<<cnt)+query(nxt[u][d^1],nxt[v][d^1],x,cnt-1);
else return query(nxt[u][d],nxt[v][d],x,cnt-1);
}
int main()
{
n=read();m=read();
trie[1]=update(trie[0],0,24); n++;//后移一位并多建一棵空树
for(int i=2;i<=n;++i)
{
int x=read(); xr^=x;
trie[i]=update(trie[i-1],xr,24);//建立可持久化Trie
//最后一个参数是数值的二进制位数,视具体数据而定
}
while(m--)
{
char ss; scanf("%s",&ss);
if(ss=='A')
{
int x=read(); xr^=x; n++;
trie[n]=update(trie[n-1],xr,24);//和上面建树一样
}
else
{
int ll=read(),rr=read(),x=read();
printf("%d\n",query(trie[ll-1],trie[rr],xr^x,24));
}
}
return 0;
}