P4735 最大异或和

P4735 最大异或和

题目描述

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

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

  1. A x:添加操作,表示在序列末尾添加一个数 \(x\),序列的长度 \(N\)\(1\)
  2. 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]维护两个值:cntid
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 的第 lenx_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();
}
posted @ 2024-12-06 11:39  liuboom  阅读(23)  评论(0)    收藏  举报