题解:洛谷 P4735 最大异或和
【题目来源】
【题目描述】
给定一个非负整数序列 \(\{a\}\),初始长度为 \(N\)。
有 \(M\) 个操作,有以下两种操作类型:
A x:添加操作,表示在序列末尾添加一个数 \(x\),序列的长度 \(N\) 加 \(1\)。Q l r x:询问操作,你需要找到一个位置 \(p\),满足 \(l \le p \le r\),使得:\(a[p] \oplus a[p+1] \oplus ... \oplus a[N] \oplus x\) 最大,输出最大值。
【输入】
第一行包含两个整数 \(N, M\),含义如问题描述所示。
第二行包含 \(N\) 个非负整数,表示初始的序列 \(A\)。
接下来 \(M\) 行,每行描述一个操作,格式如题面所述。
【输出】
假设询问操作有 \(T\) 个,则输出应该有 \(T\) 行,每行一个整数表示询问的答案。
【输入样例】
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
【输出样例】
4
5
6
【算法标签】
《洛谷 P4735 最大异或和》 #可持久化# #前缀和# #字典树Trie# #O2优化#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 600005; // 数组最大容量
int n, m, idx, cnt, s; // n,m: 输入参数,idx: 版本编号,cnt: 节点编号,s: 前缀异或和
int rt[N], ch[N * 25][2], siz[N * 25]; // rt: 每个版本的根节点,ch: Trie树左右儿子,siz: 子树大小
// 在可持久化Trie中插入一个值
void insert(int v)
{
rt[++idx] = ++cnt; // 新版本开点
int x = rt[idx - 1]; // 旧版本根节点
int y = rt[idx]; // 新版本根节点
for (int i = 23; i >= 0; i--) // 从高位到低位插入
{
int j = v >> i & 1; // 获取第i位的值
ch[y][!j] = ch[x][!j]; // 不同位继承旧版本
ch[y][j] = ++cnt; // 相同位新建节点
x = ch[x][j]; // 旧版本走位
y = ch[y][j]; // 新版本走位
siz[y] = siz[x] + 1; // 新节点子树大小增加1
}
}
// 查询[l, r]区间内与v异或的最大值
int query(int x, int y, int v)
{
int ans = 0;
for (int i = 23; i >= 0; i--)
{
int j = v >> i & 1; // 获取v的第i位
if (siz[ch[y][!j]] > siz[ch[x][!j]]) // 如果存在可以使异或结果更大的分支
{
x = ch[x][!j]; // 旧版本走位
y = ch[y][!j]; // 新版本走位
ans += (1 << i); // 累加结果
}
else // 否则走另一条路
{
x = ch[x][j];
y = ch[y][j];
}
}
return ans;
}
int main()
{
cin >> n >> m; // 读入n和m
insert(0); // 插入初始值0作为边界
// 读入初始序列并建立前缀异或和的版本
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
s ^= x; // 更新前缀异或和
insert(s); // 插入前缀异或和
}
// 处理m个操作
while (m--)
{
char op;
cin >> op;
if (op == 'A') // 添加操作
{
int x;
cin >> x;
s ^= x; // 更新前缀异或和
insert(s); // 插入新版本
}
else // 查询操作
{
int l, r, x;
cin >> l >> r >> x;
// 查询区间[l, r]内与s^x异或的最大值
cout << query(rt[l - 1], rt[r], s ^ x) << endl;
}
}
return 0;
}
【运行结果】
5 5
2 6 4 3 6
A 1
Q 3 5 4
4
A 4
Q 5 7 0
5
Q 3 6 6
6
浙公网安备 33010602011771号