bzoj 3261: 最大异或和(可持久化trie模板)

在这里插入图片描述

可持久化Trie:

一般是可持久化 01Trie。01Trie 就是维护一个数字的二进制串的trie,每个结点可以维护数字个数。在求异或最大值(最小值)时,可以从高位到低位贪心判断每一位。

例如 :从 a 数组中找一个数使得它和 valvalval 异或最大,将a数组维护到一棵 01trie 中,从高位到低位枚举valvalval的每一位 p,同时在字典树上贪心地沿根节点一路走到叶子结点:若 son[rt][p⨁1]son[rt][p \bigoplus 1]son[rt][p1] 存在(或者说num[son[rt][p⨁1]]>0num[son[rt][p \bigoplus 1]] > 0num[son[rt][p1]]>0),则rt=son[rt][p⨁1]rt = son[rt][p \bigoplus 1]rt=son[rt][p1],且答案 ans=ans⨁(1<<p)ans = ans \bigoplus (1 << p)ans=ans(1<<p),否则 rt=son[rt][p]rt = son[rt][p]rt=son[rt][p]

可持久化trie,就是在01trie的基础上,可以回退到前面的版本。其思想同于主席树(都是二叉树),当插入一个新的数字时,在trie上只会修改一条链,因此每次插入一个数字只要新建一条链即可,它的动态开点方式(trie本身就需要动态开点)和继承方式都和主席树相似。


题解:
将后缀和形式改成前缀和形式,求的是 xxx ^ b[n]b[n]b[n] ^ b[p]b[p]b[p]l−1≤p≤r−1l - 1 \leq p \leq r - 1l1pr1),b[n] 和 b[p] 代表前缀异或和。将 b 数组的前缀异或和维护到一棵可持久化 trie 上,询问时依据 sum[r][p]−sum[l−1][p]>0sum[r][p] - sum[l - 1][p] > 0sum[r][p]sum[l1][p]>0 是否成立来往下走得到答案。注意r 和 l 都需要减一,若 r - 1 = 0,前面没有建立trie,虚节点trie的查询得到的结果会是0,而不是前缀异或和,需要特判 r - 1 = 0的情况,或者将 0 插入到 trie中。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
int n,m;
int root[maxn * 2],b[maxn * 2],sz;				//trie部分 
struct trie{									//可持久化字典树 
	int sz,son[maxn * 40][2],sum[maxn * 40];
	void upd(int &rt,int v) {					
        //类似主席树的更新,每一步都要更新一个结点 
		++sz;
		son[sz][0] = son[rt][0];
		son[sz][1] = son[rt][1];
		sum[sz] = sum[rt] + 1;
		rt = sz;
		int t = rt;
		for(int i = 24; i >= 0; i--) {
			int p = (v >> i) & 1;
			++sz;
			int s = son[t][p];
			sum[sz] = sum[s] + 1;
			son[sz][0] = son[s][0];
			son[sz][1] = son[s][1];
			son[t][p] = sz;
			t = son[t][p];
		}
	}
	int qry(int l,int r,int v) {
		int ans = 0;
		int L = root[l],R = root[r];
		for(int i = 24; i >= 0; i--) {
			int p = (v >> i) & 1;
			if(sum[son[R][p ^ 1]] - sum[son[L][p ^ 1]] > 0) {
				L = son[L][p ^ 1],R = son[R][p ^ 1];
				ans |= (1 << i);
			} else {
				L = son[L][p],R = son[R][p];
			}
		}	
		return ans;
	}
}trie;
char op[2];
int main() {
	scanf("%d%d",&n,&m);
	int val = 0;
	for(int i = 1,x; i <= n; i++) {
		scanf("%d",&x);
		b[i] = b[i - 1] ^ x;
	}
	for(int i = 1; i <= n; i++) {
		root[i] = root[i - 1];
		trie.upd(root[i],b[i]);
	}
	for(int i = 1; i <= m; i++) {
		scanf("%s",op);
		int x,y,z;
		if(op[0] == 'A') {
			n++;
			scanf("%d",&x);
			b[n] = b[n - 1] ^ x;
			root[n] = root[n - 1];
			trie.upd(root[n],b[n]);
		} else {
			scanf("%d%d%d",&x,&y,&z);
			x = max(0,x - 2);
			y = max(0,y - 1);
			if(y == 0) printf("%d\n",b[n] ^ z);
			else printf("%d\n",trie.qry(x,y,b[n] ^ z));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值