版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82936103
洛谷传送门
BZOJ传送门
题目描述
给定一个非负整数序列 ,初始长度为 。
有 个操作,有以下两种操作类型:
A x
:添加操作,表示在序列末尾添加一个数 ,序列的长度 。Q l r x
:询问操作,你需要找到一个位置pp,满足 ,使得: a[p] \oplus a[p+1] \oplus … \oplus a[N] \oplus xa[p]⊕a[p+1]⊕…⊕a[N]⊕x最大,输出最大是多少。
输入输出格式
输入格式:
第一行包含两个整数
,含义如问题描述所示。
第二行包含
个非负整数,表示初始的序列
。
接下来
行,每行描述一个操作,格式如题面所述。
输出格式:
假设询问操作有 个,则输出应该有 行,每行一个整数表示询问的答案。
输入输出样例
输入样例#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
说明
对于测试点
,
。
对于测试点
,
。
对于测试点
,
。
其中测试点
保证没有修改操作。
。
解题分析
直接处理后缀异或和不好办, 我们维护前缀和, 这样就成了 中的一个元素和(指定元素 所有元素异或和)的最大异或和。
这个玩意当然可以用主席树二分每位贪心得到, 然而这个复杂度是 的, 显然不是很优。
这里有一种更优雅的方法: 可持久化 树。 我们按二进制下元素的每一位从高到低插入 树里, 按主席树的方法可持久化, 查询时贪心走反方向即可。
注意这里有 的情况, 这个时候可以取所有元素,相当于在 树上取到 , 需要特判。
代码如下:
#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;
}