- admin's blog
【模板】可持久化0/1tire
- @ 2024-4-25 21:30:35
【模板】可持久化0/1tire
题目:题目详情 - 最大异或和 - Hydro (v100.vip)
题意:可持久化数据结构,求从l,r区间选一个位置开始,一直异或到最后然后与x进行异或,求最大为多少。
分析:
- [l,r] 作为起点,N作为终点,那么 r+1~n 的值一定会与x进行异或,是一个定值。提前处理。
- 要使异或值最大,那肯定是越前的位数就要相反。即建立树的时候从高位开始建。
- 在1和2的基础上,要使得更快更有效的方法,怎么实现??? 前缀和思想 !! 建树的时候,建的就是前缀异或和的树, 求答案的时候,就用[ l , r ]区间的值去和 x^s[n]看怎么走异或值最大 如果用s[y]代表前缀异或和的话。那么最大值=s[n]^x^ s ( l , r ) = ( a[1]^a[2]……a[n] ) ^ x ^ ( a[1] ^ a[2] ^ …… ^ a[ (l,r) ] ),求由于s[n]^x为定值,我们只需要从r位置开始求前缀异或和后尽量与s[n]^相反的值就可以了 直接上代码
insert(int x,int y,int i)
插入前缀异或和 x是当前插入的位置编号,y是上一个节点的编号,i是插入的是第几个数(用于判断是否超出范围)
void insert( int x,int y,int i )
{ vesion[x]=i;
for(int k=len;k>=0;k--)
{ int c=s[i]>>k&1;
ch[ x ][ !c ]=ch[ y ][ !c ]; //离他本身最近的那个节点但值相反的位置在哪
ch[ x ][ c ]=++idx; //为自己建节点。
x=ch[x][c],y=ch[y][c];
vesion[x]=i; //这个位置是第i个
}
}
插入的时候插入前缀异或和,并记录前缀相同的位置
quire( int x,int l,int v )
查询函数,要求最左端不超过l,并且该位置与v异或值相反的最大值。
int quire( int x,int l,int v )
{
int ans=0; //记录答案
for(int k=len;k>=0;k--) //从高位开始异或
{
int c=(v>>k)&1; //获取这个位置的二进制值
if( vesion[ch[x][!c]]>=l ) //如果往反方向走没有走出l的位置
x=ch[x][!c],ans+=1<<k; //走过去,并且答案增加
else x=ch[x][c]; //反之走不过去,答案不更新
}
return ans;
}
code
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+12,len=23;
int ch[N*25][2],s[N],vesion[N*25],idx,root[N];
void insert( int x,int y,int i )
{
vesion[x]=i;
for(int k=len;k>=0;k--)
{
int c=s[i]>>k&1;
ch[ x ][ !c ]=ch[ y ][ !c ];
ch[ x ][ c ]=++idx;
x=ch[x][c],y=ch[y][c];
vesion[x]=i;
}
}
int quire( int x,int l,int v )
{
int ans=0;
for(int k=len;k>=0;k--)
{
int c=(v>>k)&1;
if( vesion[ch[x][!c]]>=l )
x=ch[x][!c],ans+=1<<k;
else x=ch[x][c];
}
return ans;
}
int n,m;
int main(){
char p;
int x,l,r;
vesion[0]=-1;
root[0]=++idx;
insert(root[0],0,0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>x;
s[i]=s[i-1]^x;
root[i]=++idx;
insert( root[i],root[i-1],i );
}
while(m--)
{
cin>>p;
if( p=='A' )
{
cin>>x;
n++;
s[n]=s[n-1]^x;
root[n]=++idx;
insert( root[n],root[n-1],n );
}
else
{
cin>>l>>r>>x;
cout<<quire( root[r-1],l-1,s[n]^x )<<endl;
}
}
}