P4735 最大异或和 可持久化Trie模板题

题目点这里

他要求一个区间内的位置到结尾处异或和的最大值,我们转换一下,设S[i]表示前i个位置上的数异或起来的结果,即异或值的前缀和。
于是题目的要求a[p] ^ a[p+1] ^… a[n] ^ x = s[p-1] ^ s[n] ^ x.。于是题目就相当于变成了,已知一个值val = s[n]^x,求一个位置p满足 l-1 =< p <= r-1,使得s[p] ^ val的这个值最大
这样的话我们只需要将trie可持久化后,每次区间询问位置合法(即两个版本间是否有合法位置)是查询val每一位上相反的位置的区间条件是否合法,用一个cnt数组来记录trie的尾结点对应的版本的信息,从而达到p的区间要求
最后,如果询问的区间左端点是1的话,可能会从0号去的最大的异或和,因此我们需要,在执行插入操作的时候,预先处理插入一个0号节点来解决这个问题。

具体操作实现看代码:
可以参考算法竞赛进阶指南P252页

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 6e5+7;
//可持久化Trie
int n,m;
int trie[MAXN*24][2],root[MAXN<<1],cnt[MAXN*24],size;
int s[MAXN<<1];//异或和

void insert(int now,int pre,int k,int x)
{
    
    
	if(k < 0) return ;
	int i = (x>>k)&1;//把第k位拿上来
	trie[now][!i] = trie[pre][!i];//之前的都连在一起
	trie[now][i] = ++size;//新插入的单独开节点
	cnt[trie[now][i]] = cnt[trie[pre][i]]+1;//记录当前的版本
	insert(trie[now][i],trie[pre][i],k-1,x);//递归插入
}

int query(int r,int l,int k,int x)
{
    
    
	if(k < 0) return 0;
	int i = (x>>k)&1;
	if(cnt[trie[r][!i]] > cnt[trie[l][!i]])//代表这个区间可行
		return (1<<k) + query(trie[r][!i],trie[l][!i],k-1,x);
	else
		return query(trie[r][i],trie[l][i],k-1,x);
}

int main()
{
    
    
	scanf("%d%d",&n,&m);
	//要把0先插入trie中
	root[0] = ++size;
	insert(root[0],0,24,0);
	for(int i = 1;i <= n;i ++){
    
    
		int x;
		scanf("%d",&x);
		s[i] = s[i-1]^x;
		root[i] = ++size;
		insert(root[i],root[i-1],24,s[i]);
	}
	while(m--){
    
    
		char op;int l,r,x;
		cin>>op;
		switch(op){
    
    
			case 'A':
				scanf("%d",&x);
				n++;//在序列尾添加元素的话 就是在尾部申请一个新的节点
				s[n] = s[n-1]^x;
				root[n] = ++size;
				insert(root[n],root[n-1],24,s[n]);
				break;
			case 'Q':
				scanf("%d%d%d",&l,&r,&x);
				l--,r--;
				//printf("-----\n");
				int ans;
				if(l == 0) ans = query(root[r],0,24,s[n]^x);
				else ans = query(root[r],root[l-1],24,s[n]^x);
				//printf("-----\n");
				printf("%d\n",ans);
				break;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45672411/article/details/108487032