【洛谷P4735】最大异或和

题目

题目链接:https://www.luogu.com.cn/problem/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\leq 300000,0\leq a_i\leq 10^7\)

思路

可持久化 Trie 板子题。到现在才会这个玩意退役了退役了。
和可持久化线段树基本一样的思路,每次插入的时候新建节点,然后将上一个版本的点复制过来,然后继续往下递归处理。
回到本题,显然做前缀和,问题转化为在 \([l-1,r-1]\) 中找到一个位置 \(p\) 使得 \(a_p\ \mathrm{xor}\ a_n\ \mathrm{xor}\ x\) 最大。
那么就直接在版本 \(r\) 的 Trie 上查询就可以解决右端点,为了确保不要查询到左端点,可以在 Trie 树上每一个节点维护一个 \(last\) 表示这棵子树中,所表示的数的下表最大的值,然后每次查询的时候只往 \(last\geq l-1\) 的走就可以了。
时间复杂度 \(O(m\log n)\)

代码

#include <bits/stdc++.h> 
using namespace std;

const int N=1000010,LG=25;
int n,m,a[N],rt[N];
char opt[3];

struct Trie
{
	int tot,ch[N*LG][2],last[N*LG];
	Trie() { tot=1; rt[0]=1; }
	
	int ins(int dep,int now,int v,int k)
	{
		int x=++tot,id=((v>>dep)&1); 
		ch[x][0]=ch[now][0]; ch[x][1]=ch[now][1]; last[x]=k;
		if (dep<0) return x;
		ch[x][id]=ins(dep-1,ch[now][id],v,k);
		return x;
	}
	
	int query(int dep,int x,int l,int v)
	{
		if (dep<0) return a[last[x]];
		int id=((v>>dep)&1);
		if (ch[x][id^1] && last[ch[x][id^1]]>=l)
			return query(dep-1,ch[x][id^1],l,v);
		else
			return query(dep-1,ch[x][id],l,v);
	}
}trie;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]); a[i]^=a[i-1];
		rt[i]=trie.ins(LG,rt[i-1],a[i],i);
	}
	while (m--)
	{
		scanf("%s",opt);
		if (opt[0]=='A')
		{
			scanf("%d",&a[++n]); a[n]^=a[n-1];
			rt[n]=trie.ins(LG,rt[n-1],a[n],n);
		}
		else
		{
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			int y=trie.query(LG,rt[r-1],l-1,a[n]^x);
			printf("%d\n",a[n]^x^y);
		}
	}
	return 0;
}
posted @ 2021-02-14 21:35  stoorz  阅读(24)  评论(0编辑  收藏  举报