最大异或和【可持久化Trie】

>Link

luogu P4735


>Description

给定一个非负整数序列 { a } \{a\} {a},初始长度为 n n n

m m m 个操作,有以下两种操作类型:

  1. A x:添加操作,表示在序列末尾添加一个数 x x x,序列的长度 n + 1 n+1 n+1
  2. Q l r x:询问操作,你需要找到一个位置 p p p,满足 l ≤ p ≤ r l \le p \le r lpr,使得: a [ p ] ⊕ a [ p + 1 ] ⊕ . . . ⊕ a [ N ] ⊕ x a[p] \oplus a[p+1] \oplus ... \oplus a[N] \oplus x a[p]a[p+1]...a[N]x 最大,输出最大是多少。

N , M ≤ 300000 N,M \le 300000 N,M300000 0 ≤ a [ i ] ≤ 1 0 7 0 \le a[i] \le 10^7 0a[i]107


>解题思路

s u m i sum_i sumi 为前 i i i 位的异或前缀和,对于题目的第二个操作,我们可以根据异或的性质,变成: s u m p − 1 ⊕ s u m n ⊕ x sum_{p-1} \oplus sum_n \oplus x sump1sumnx

那我们维护 s u m sum sum 就行了
运用主席树,把每个 s u m i sum_i sumi 塞进可持久化的Trie,维护前 i i i 位所有的前缀
查询时,要求位置 p p p [ l , r ] [l,r] [l,r] 中,那就只在前 r r r 位的Trie上搜,注意还要维护当前节点的子树中最大的序号,检查是否大于等于 l l l,然后就直接贪心在Trie上从上往下搜


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 600000
#define M 25
using namespace std;

int n, m, cnt, a[N * 30], son[N * 30][2], mx[N * 30], root[N * 30], ans, p; 

void insert (int u, int v, int dep, int w)
{
	if (dep < 0) return;
	int x = (w >> dep) & 1;
	son[u][x ^ 1] = son[v][x ^ 1];
	son[u][x] = ++cnt;
	mx[son[u][x]] = mx[son[v][x]] + 1;
	insert (son[u][x], son[v][x], dep - 1, w);
}
int ask (int u, int v, int dep)
{
	if (dep < 0) return 0;
	int x = (p >> dep) & 1;
	if (mx[son[v][x ^ 1]] > mx[son[u][x ^ 1]])
	  return (1 << dep) + ask (son[u][x ^ 1], son[v][x ^ 1], dep - 1);
	else
	  return ask (son[u][x], son[v][x], dep - 1);
}

int main()
{
	scanf ("%d%d", &n, &m);
	root[0] = ++cnt;
	insert (root[0], 0, M, 0);
	for (int i = 1; i <= n; i++)
	{
		scanf ("%d", &a[i]);
		a[i] ^= a[i - 1];
		root[i] = ++cnt;
		insert (root[i], root[i - 1], M, a[i]);
	}
	string c; int l, r, x;
	while (m--)
	{
		cin >> c;
		if (c[0] == 'A')
		{
			n++;
			scanf ("%d", &a[n]);
			a[n] ^= a[n - 1];
			root[n] = ++cnt;
			insert (root[n], root[n - 1], M, a[n]);
		}
		else
		{
			scanf ("%d%d%d", &l, &r, &x);
			l--, r--;
			p = a[n] ^ x;
			if (l == 0) printf ("%d\n", ask (0, root[r], M));
			else printf ("%d\n", ask (root[l - 1], root[r], M));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值