可持久化01Trie

例题:

解释:

首先这里要求连续异或,所以存储前缀异或和数组。首先的话,我们只考虑前r个版本的Trie,所以以root[r]为根节点的Trie就是1到r位置数。但是,还有一个l左端点,所以我们对于每一个节点来说,额外存储一下max_id,表示的就是以当前节点为子树的最大的i。那这个有什么用呢?

这里我们都是动态开点,也就是假设当前我们的数是1,所以我们要向下遍历找到一个0,由于我们是有限制条件的,所以对于每一个节点,加入他的0的这个儿子的max_id大于我们的限制条件,也就是这个子树里面存在一个s{i],它的下标大于或等于我们的限制条件,所以我们就可以往0这个方向走。

动态开点思路:对于每一个s[i]我们都去开这个数的01串,也就是一个链子,刚开始是我们的根节点,也就是我们的版本(第几版)。假设当前数是(二进制:101010),我们先开了一个根节点(第几版),于是我们继续开一个1这个节点,如果它的上一个版本中的根节点有0这个儿子,那么我们就让上一个版本的0这个儿子指向当前的根节点(也就是直接拷贝)。然后继续开点,开0这个节点,只要遇到不同的,就直接拷贝,如果上一个版本存在当前的链子的前半部分,我们也是必须开的,只有不同的我们才会去复制。

那么这样执行下来,就会存下每一个版本的Trie,也就是我们的可持久化Trie,那么这到底有什么用呢?下面就用上面这个例题来介绍它的用处!

由于当前是由区间限制的,不像一组数据中找两个数他们的异或值最大,没有区间限制的话,用我们的Trie就可以了。首先如果我们只考虑1到r这个条件的话,那么直接从第r个版本的Trie开始找一个数就可以了。然后在考虑数必须大于等于l这个条件,由于我们存储了max_id,也就是下一个节点的max_id,只要大于等于限制条件,就可以遍历这个节点。假设max_id是3,限制条件是2,表示的是这个字数有一个数它的下标是3,所以就可以遍历这个节点,反之,就不可以遍历,因为每一个节点它的下标大于限制条件,都是l前面的数,所以就不能遍历。

所以,直接上代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 600010,M = N*25;
int n, m;
int tr[M][2], idx;
int root[N];
int s[N];
int max_id[M];

void insert(int i, int k, int p, int q)
{
	if (k < 0)
	{
		max_id[q] = i;
		return ;
	}
	int u = s[i] >> k & 1;
	if (p)tr[q][u^1] = tr[p][u^1];
	tr[q][u] = ++idx;
	insert(i, k - 1, tr[p][u], tr[q][u]);
	max_id[q] = max(max_id[tr[q][0]],max_id[tr[q][1]]);
}

int query(int root, int limit, int c)
{
	int p = root;
	for (int i = 23; i >= 0; i--)
	{
		int u = c >> i & 1;
		if (max_id[tr[p][!u]] >= limit && tr[p][!u])p = tr[p][!u];
		else p = tr[p][u];
	}
	return c ^ s[max_id[p]];
}

int main()
{
	cin >> n >> m;

	root[0] = ++idx;
	insert(0,23,0,root[0]);

	for (int i = 1; i <= n; i++)
	{
		int x;
		scanf("%d", &x);
		root[i] = ++idx;
		s[i] = s[i - 1] ^ x;
		insert(i, 23, root[i - 1], root[i]);
	}

	char op[2]; int l, r, x;
	while (m--)
	{
		scanf("%s", op);
		if (*op == 'A')
		{
			scanf("%d", &x);
			n++;
			root[n] = ++idx;
			s[n] = s[n - 1] ^ x;
			insert(n, 23, root[n - 1], root[n]);
		}
		else
		{
			scanf("%d%d%d", &l, &r, &x);
			printf("%d\n", query(root[r - 1], l - 1, s[n] ^ x));
		}
	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值