P4735 最大异或和
P4735 最大异或和
题目描述
给定一个非负整数序列 \(\{a\}\),初始长度为 \(N\)。
有 \(M\) 个操作,有以下两种操作类型:
A x:添加操作,表示在序列末尾添加一个数 \(x\),序列的长度 \(N\) 加 \(1\)。Q l r x:询问操作,你需要找到一个位置 \(p\),满足 \(l \le p \le r\),使得:\(a[p] \oplus a[p+1] \oplus ... \oplus a[N] \oplus x\) 最大,输出最大值。
输入格式
第一行包含两个整数 \(N, M\),含义如问题描述所示。
第二行包含 \(N\) 个非负整数,表示初始的序列 \(A\)。
接下来 \(M\) 行,每行描述一个操作,格式如题面所述。
输出格式
假设询问操作有 \(T\) 个,则输出应该有 \(T\) 行,每行一个整数表示询问的答案。
输入输出样例 #1
输入 #1
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
输出 #1
4
5
6
说明/提示
- 对于所有测试点,\(1\le N,M \le 3\times 10 ^ 5\),\(0\leq a_i\leq 10 ^ 7\)。
Solution:
可持久化Trie模板题
维护一棵0/1Trie:
每个节点t[x][y]维护两个值:cnt 和 id。
cnt :表示到x这个节点所在的根(可理解为时间),y(0/1)出现了几次(前缀和);
id :表示当前数字(x,y)被存在哪个容器中(相当于一个指针)
可持久化Trie:
插入方式与普通Trie基本相同:
对于一个节点,新建一个点表示当前节点:
对于存在的值:t[now][x]={t[last][x].cnt+1,++tot};
对于不存在的值 t[now][x1]=t[last][x1];(直接复制过来)
查询与可持久化线段树类似
---------------------------------------------------------------------------------
对于本题:我们每次对 pos 这个位置插入的是 [1,x] 区间的所有数的异或
对于每一次查询:很容易想到我们要按二进制从高往低贪心。
对于 x 的第 len 位 x_len,如果在 [p,r] 的所有数的异或中(设为 y )能找到 x_len^1。
则说明 y_len^x_len=1,即 ans的第len位可取1,根据贪心,我们应该取
而这题有一个很神奇的地方:
if(t[l][y^1].cnt<t[r][y^1].cnt)
{
// 当前为第len位
return (1<<len)+query(t[l][y^1].id,t[r][y^1].id,len-1,x);
}
只要满足 if 中的条件,则说明在[l,r]的a中(原题目意义下),出现过能改变 len 这一位的数。
而这个神奇的性质就是:
既然它可以使得前缀改变,那它也一定能改变后缀
所以只要满足上述条件,则说明y中有它.
剩下就是一些细节了
code:
#include<bits/stdc++.h>
const int N=3e5+5;
using namespace std;
char c[10];
int n,m,tot;
int rt[N],a[N];
struct Trie{
int cnt,id;
}t[N*30][2];
void ins(int now,int last,int len,int x)
{
if(len<0)return ;
int y=(x>>len)&1;// (当前数字对1的xor)
t[now][y^1]=t[last][y^1];//对于这一位没有的数字,将前面的节点复制过来
t[now][y].id=++tot;
t[now][y].cnt=t[last][y].cnt+1;//对于这一位上的"当前数字",记录前缀和的个数
ins(t[now][y].id,t[last][y].id,len-1,x);
}
int query(int l,int r,int len,int x)
{
if(len<0)return 0;
int y=(x>>len)&1;
if(t[l][y^1].cnt<t[r][y^1].cnt)//y^1在[l,r]之间出现过,根据贪心,取了y^1与y异或一定最优
{
// 当前为第len位
return (1<<len)+query(t[l][y^1].id,t[r][y^1].id,len-1,x);
}
else
{
return query(t[l][y].id,t[r][y].id,len-1,x);
}
}
void work()
{
cin>>n>>m;
rt[0]=++tot;
ins(rt[0],0,25,0);
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
a[i]=a[i-1]^x;
rt[i]=++tot;
ins(rt[i],rt[i-1],25,a[i]);
}
for(int i=1,x,l,r,ans;i<=m;i++)
{
scanf("%s",c+1);
if(c[1]=='A')
{
scanf("%d",&x);
rt[++n]=++tot;
a[n]=a[n-1]^x;
ins(rt[n],rt[n-1],25,a[n]);
}
else
{
scanf("%d%d%d",&l,&r,&x);
l--,r--;//序列从0开始
x^=a[n];
if(l==0)
{
ans=query(0,rt[r],25,x);
}
else
{
//l-1:当前可行的区间是[l,r],所以把l-1当作前缀
ans=query(rt[l-1],rt[r],25,x);
}
printf("%d\n",ans);
}
}
}
int main()
{
work();
}

浙公网安备 33010602011771号