[Luogu P4735] [BZOJ 3261] 最大异或和

4 篇文章 0 订阅
2 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

给定一个非负整数序列 { 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:询问操作,你需要找到一个位置pp,满足 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 N,M N,M,含义如问题描述所示。
第二行包含 N N N个非负整数,表示初始的序列 A A A
接下来 M M M 行,每行描述一个操作,格式如题面所述。

输出格式:

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

输入输出样例

输入样例#1:
5 5
2 6 4 3 6
A 1 
Q 3 5 4 
A 4
Q 5 7 0 
Q 3 6 6 
输出样例#1:
4
5
6

说明

对于测试点 1 − 2 1-2 12 N , M ≤ 5 N,M \le 5 N,M5
对于测试点 3 − 7 3-7 37 N , M ≤ 80000 N,M \le 80000 N,M80000
对于测试点 8 − 10 8-10 810 N , M ≤ 300000 N,M \le 300000 N,M300000
其中测试点 1 , 3 , 5 , 7 , 9 1, 3, 5, 7, 9 1,3,5,7,9保证没有修改操作。
0 ≤ a [ i ] ≤ 1 0 7 0 \le a[i] \le 10^7 0a[i]107

解题分析

直接处理后缀异或和不好办, 我们维护前缀和, 这样就成了 [ l − 1 , r − 1 ] [l-1,r-1] [l1,r1]中的一个元素和(指定元素 x o r xor xor 所有元素异或和)的最大异或和。

这个玩意当然可以用主席树二分每位贪心得到, 然而这个复杂度是 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))的, 显然不是很优。

这里有一种更优雅的方法: 可持久化 T r i e Trie Trie树。 我们按二进制下元素的每一位从高到低插入 T r i e Trie Trie树里, 按主席树的方法可持久化, 查询时贪心走反方向即可。

注意这里有 l = 1 l=1 l=1的情况, 这个时候可以取所有元素,相当于在 T r i e Trie Trie树上取到 0 0 0, 需要特判。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 300500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
struct Node {int son[2], sum;} tree[MX << 6];
int n, m, cnt, pre[MX << 1], root[MX << 1];
namespace Trie
{
	void insert(int &now, R int pre, R int tar, R int digit)
	{
		now = ++cnt; R int id = (tar >> digit) & 1;
		tree[now] = tree[pre]; tree[now].sum++;
		if(digit < 0) return;//多插一层, 因为要有这个儿子上一层才能有贡献。
		insert(tree[now].son[id], tree[pre].son[id], tar, digit - 1);
	}
	int query(R int pre, R int now, R int tar, R int digit)
	{
		if(digit < 0) return 0;
		R int id = !((tar >> digit) & 1);
		if(tree[tree[pre].son[id]].sum < tree[tree[now].son[id]].sum)
		return (1 << digit) + query(tree[pre].son[id], tree[now].son[id], tar, digit - 1);
		else return query(tree[pre].son[id ^ 1], tree[now].son[id ^ 1], tar, digit - 1);
	}
}
char buf[5];
int main(void)
{
	int a, b, c;
	in(n); in(m);
	for (R int i = 1; i <= n; ++i)
	{
		in(a); pre[i] = pre[i - 1] ^ a;
		Trie::insert(root[i], root[i - 1], pre[i], 26);
	}
	for (R int i = 1; i <= m; ++i)
	{
		scanf("%s", buf);
		if(buf[0] == 'A')
		{
			in(a);
			pre[n + 1] = pre[n] ^ a; ++n;
			Trie::insert(root[n], root[n - 1], pre[n], 26);
		}
		else
		{
			in(a), in(b), in(c); --a, --b;
			if(!a) 	printf("%d\n", std::max(Trie::query(0, root[b], c ^ pre[n], 26), c ^ pre[n]));
			else printf("%d\n", Trie::query(root[a - 1], root[b], c ^ pre[n], 26));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值