可持久化字典树 AcWing256 最大异或和

 分析:这个题乍一看就是一个普普通通的字典树,但是仔细读题后会发现只是用单纯的字典树好像解决不了这个问题,因为字典树在插入之后无法分辨插入的顺序,它不像数组一样每插入一个数就对应一个下标,它是把所有数字的二进制表示都混在了一起,所以我们就要考虑保存它的历史版本,然后就有了可持久化字典树这个东东。

具体的细节见代码的注释部分,这里需要注意一点,我们在建立第一个版本之前要先建立第0个版本,因为我们算前缀和的时候是把第0个也算进去了,而且,如果当前让我们选择是区间如果是1到某一个数,那按照我们的思路会查找第0个版本,如果我们没有建立第0个版本那结果一定错误。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N =6e5+10;
const int M=N*25;
int s[N],n,m,idx;//s[x]是一个异或前缀和数组,表示从第1个数一直异或到第x个数 
int son[M][2],max_id[M],root[N];
void insert(int i,int p,int k,int q)//i记录当前插入的元素是第几个,p是上一个版本的根节点,q是当前版本的根节点,k是当前要插入的数的位数 
{
	if(k<0)//数字已经处理完了 
	{
		max_id[q]=i;
		return ;
	}
    int v=s[i]>>k&1;
    if(p)
    son[q][v^1]=son[p][v^1];//继承上一个版本的字典树 
    son[q][v]=++idx;//递归创建当前版本字典树 
	insert(i,son[p][v],k-1,son[q][v]);
    max_id[q]=max(max_id[son[q][0]],max_id[son[q][1]]);//max_id[]数组维护的是从根节点到当前位置的最大插入顺序的编号 
}
//因为题目告诉我们要从l到r这个区间中选一个值一直异或到最后
//我们已经提前算出s[n]^x,假设我们选的是第x个位置上的数,那么结果就应该是是s[n]^x^s[x-1]
//由此可见我们选择的范围是l-1~r-1,这里我们选用root[r-1]为根节点,保证了范围是从0~r-1
//然后我们就需要限制左端点要在>=l-1的位置,所以我们要判断一下当前节点的max_id[p]是否>=l-1 
int query(int root,int c,int l)//root是选择的版本的根节点,它限制了选择的右端点,c是s[n]^x,l限制了左端点 
{
	int p=root;
	for(int i=23;i>=0;i--)
	{
		int v=c>>i&1;
		if(max_id[son[p][v^1]]>=l)
		p=son[p][v^1];
	    else
	    p=son[p][v];
	}
	return c^s[max_id[p]];
}
int main()
{
  	cin>>n>>m;
  	root[0]=++idx;
  	max_id[0]=-1;
  	insert(0,0,23,root[0]);
  	for(int i=1;i<=n;i++)
  	{
  		int x;
  		scanf("%d",&x);
  		s[i]=s[i-1]^x;
  		root[i]=++idx;
  		insert(i,root[i-1],23,root[i]);
    }
    char op[2];
    int l,r,x;
    for(int i=1;i<=m;i++)
    {
    	scanf("%s",op);
		if(op[0]=='A')
		{
			scanf("%d",&x);
			n++;
			s[n]=s[n-1]^x;
			root[n]=++idx;
			insert(n,root[n-1],23,root[n]);
		 } 
		 else
		 {
		 	scanf("%d%d%d",&l,&r,&x);
		 	cout<<query(root[r-1],s[n]^x,l-1)<<endl;
		 }
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值