可持续化Trie_区间异或最大_bzoj3261

最大异或和问题解析
本文介绍了一种解决最大异或和问题的方法,利用前缀异或和与可持久化Trie树实现区间异或最大值的高效查询,适用于动态序列更新场景。

【bzoj3261】最大异或和

Description

给定一个非负整数序列 {a},初始长度为 N。
有   M个操作,有以下两种操作类型:
1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1。
2 、Q l r x:询问操作,你需要找到一个位置 p,满足 l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。

Input

第一行包含两个整数 N  ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。

Output

假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。

Sample Input

5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
对于测试点 1-2,N,M<=5 。

对于测试点 3-7,N,M<=80000 。
对于测试点 8-10,N,M<=300000 。

其中测试点 1, 3, 5, 7, 9保证没有修改操作。
对于 100% 的数据, 0<=a[i]<=10^7。

Sample Output

4
5
6

HINT

对于      100%  的数据,     0<=a[i]<=10^7  

对于求区间异或的最大值这种问题,我们首先可以这样思考,我们要知道这个区间当中哪一段开始是最大的,那么我们需要维护一下他的前缀异或和。比如题目中给定的a[1]...a[n]我们用b[i]表示a[1]^...a^[i] 那么我们用b[i-1]^b[n]就可以得到a[i]^...^a[n]的值了, 然后再异或上题目中给定的x。到这里我们分析可以得到我们需要计算的其实是b[i-1]^b[n]^x的最大值,因为b[n]^x的值是确定的,那么我们只需要计算b[i-1]中i-1取哪一个的时候可以与b[n]^x异或的值最大即可。而我们又知道i是从题目中给定的l到r的范围内确定的,因此i-1属于[l-1,r-1],这样我们就确定好了区间的范围了。然后我们怎么维护每一个b[i]呢? 可以考虑可持久化的数据结构,这里用trie树因为存二进制位,讲到可持久化数据结构,应该有学过主席树,没有的话可以看我的上一篇,可持久化的思想就是保存每一个修改/更新的版本,只在每一个版本上进行需要更新的节点。 好了,看代码,我用的是非指针的,并且不是递归,感觉这样快点,虽然代码变长了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int a[maxn],b[maxn];
struct Node{
	int sum;
	int l;
	int r;
	Node(){
		l=0,r=0,sum=0;
	}
}node[maxn<<5];
int root[maxn],cnt,K=5,n,m;//K表示該題的数据范围的对应二进制的位数 
char ch[10];
int update(int last,int val){
	int now = ++cnt;//cnt表示的是该节点的标号  
	node[now] = node[last];//一开始的的时候新版本的根节点指向上一个版本的根节点
	int temp = now;
	for(int i=K;i>=0;i--){
		int bit = (val>>i)&1;
		if(bit){
			node[temp].l = node[last].l;//左边的不去修改,一直复制上一个版本的信息即可
			node[temp].r = ++cnt;//当前版本中该位置需要有更新所以新建节点 
			temp = node[temp].r, last = node[last].r; 
			node[temp].sum = node[last].sum+1;//不能是自己++ 需要保持与上一个版本的信息一致 所以在上一个版本基础上++ 
		}
		else{//该位是0 往左走  
			node[temp].r = node[last].r;
			node[temp].l = ++cnt;
			temp = node[temp].l, last = node[last].l;
			node[temp].sum = node[last].sum+1; 
		}
	}
	return now;//返回当前版本的根节点的标号 
}
int query(int pre, int now,int val){
	int ans = 0;
	for(int i=K;i>=0;i--){
		int bit = (val>>i)&1;
		if(bit){//当前位置是1 则需要异或0 可以得到max 所以往左走 
			if(node[now].l==0){//左节点没有 往又走 
				now = node[now].r;
				pre = node[pre].r;
				continue;
			}
			int count = node[node[now].l].sum - node[node[pre].l].sum;//当前版本与之前版本的个数差 
			if(count>0){
				ans += (1<<i);
				now = node[now].l;
				pre = node[pre].l;
			}
			else{
				now = node[now].r;
				pre = node[pre].r;
			}
		}
		else{//当前位是0 需要异或1得到最大  
			if(node[now].r==0){//右节点没有 往左走 
				now = node[now].l;
				pre = node[pre].l;
				continue;
			}
			int count = node[node[now].r].sum - node[node[pre].r].sum;//当前版本与之前版本的个数差 
			if(count>0){
				ans += (1<<i);
				now = node[now].r;
				pre = node[pre].r;
			}
			else{
				now = node[now].l;
				pre = node[pre].l;
			}
		}
	}
	return ans;
}
void init(){
	cnt = 0;//每一次都需要重置0的个数 
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(root,0,sizeof(root));
}
int main(){
	while(~scanf("%d%d",&n,&m)){
//		n++;
//		root[1] = update(root[0],0);//一开始要插入为0  因为最初异或和是0  
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			b[i] = b[i-1]^a[i];
			root[i] = update(root[i-1],b[i]); 
//			printf("%d\n",root[i]);
		}
		for(int i=1;i<=m;i++){
			scanf("%s",ch);
			if(ch[0] == 'A'){
				n+=1;
				scanf("%d",&a[n]);
				b[n] = b[n-1]^a[n];
				root[n]  = update(root[n-1],b[n]);
			}
			else{
				int l,r,x;
				scanf("%d%d%d",&l,&r,&x);//因为题目中需要求a[i]^..a[n]^x 最大
				//我们将b[n]作为记录a[1]^..a[n]的前缀和 所以要求该式子 我们只需要求 b[i-1]^b[n]^x 最大即可
				//所以我们要定位i-1 且 l<=i<=r 所以 l-1<=i-1<=r-1 因此在下面的区间搜寻即可 
				int ans = query(root[l-2],root[r-1],x^b[n]);
				printf("%d\n",ans);
			}
		}
	}
} 


### Trie树实现区间异或最大值的算法 Trie树可以用来高效求解区间内的最大异或值问题。以下是基于Trie树的核心思路以及其实现方法。 #### 核心思想 为了找到给定数组 `arr` 中任意两个数的最大异或值,可以通过构建一棵特殊的Trie树来优暴力搜索的时间复杂度。这棵树通常被称为 **01-Trie树** 或者 **二进制字典树**,它按照整数的二进制位逐位存储数据[^2]。 对于每一个查询 `(l, r)` 表示在子数组 `[arr[l], arr[r]]` 的范围内找两数使得它们的异或结果最大,我们可以采用如下策略: 1. 将当前范围内的所有数值依次插入到Trie树中; 2. 对于每个新加入的数字,在已经存在的Trie树上尝试寻找异或最大的另一个数字; 此过程依赖于这样一个事实:如果希望得到尽可能大的异或值,则应优先选择高位上的不同比特位(即当某一位为‘1’时倾向于匹配‘0’, 反之亦然)[^3]。 下面给出具体的Python代码实现这一逻辑: ```python class Node: def __init__(self): self.children = {} def insert(root, num): node = root for i in range(31, -1, -1): # Assuming numbers are within 32 bits. bit = (num >> i) & 1 if bit not in node.children: node.children[bit] = Node() node = node.children[bit] def find_max_xor_pair(root, nums): max_xor = 0 for num in nums: current_node = root xor_value = 0 for i in range(31, -1, -1): bit = (num >> i) & 1 toggled_bit = 1 - bit if toggled_bit in current_node.children: xor_value = (xor_value << 1) | 1 current_node = current_node.children[toggled_bit] elif bit in current_node.children: xor_value <<= 1 current_node = current_node.children[bit] else: break max_xor = max(max_xor, xor_value) return max_xor # Example usage of the functions above to solve maximum XOR pair problem using a trie tree structure. root = Node() nums_example = [3, 10, 5, 25, 2, 8] for number in nums_example: insert(root, number) result = find_max_xor_pair(root, nums_example) print(f"The Maximum Pairwise XOR is {result}") ``` 上述程序定义了一个简单的Node类表示Trie树中的节点,并提供了insert函数用于向Trie树添加新的整数项,find_max_xor_pair则是遍历输入列表并计算可能获得的最大XOR值的方法之一[^4]。 #### 注意事项 - 构建Trie树时需考虑目标平台支持的数据宽度,默认这里假设不超过32位无符号整型。 - 插入过程中是从最高有效位开始逐步深入至最低有效位。 - 查询阶段同样遵循从高往低的原则选取路径以达成最终较高的异或得分。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值