传送门:最大异或和
前言:
这道题是一个经典的可持久化的题。可持久化的思想就是通过空间优化存储每一时刻的状态,以便在后续的询问中能够用快速查询。
思路:
此题问的是的最大值。
那么先不考虑给定的范围。将问题转化为找到一个位置,使得最大。
那么这个问题怎么解决呢?
这里需要补充一个前置知识:
那么我们可以先预处理一个数组,用于存储前n项的异或和,再通过逐位去对比,找到一个满足条件的最大值即可。
举个小小的栗子:
假设的二进制表示为 1001010。
那么最理想的 能使 最大的的二进制表示为0110101。
但是不可能一定存在最理想的情况,所以就要每一位的去判断前面是否存在这种情况。
现在,思路已经出来了,建一颗01字典树就ok了,因为是详解,所以就画了一个字典树的建树图示。
定义:为01字典树,里面存储的是达到当前状态的距离n最近的位置。
假设我们要建立的是:1001010,0010101,1001001
建树图示:(n=1,2,3的根节点实际上是同一个点,这里为了更好的表示,就画了三个不同的)
但是,擅长折磨我们的出题人可不会如此友善。
现在题目是给定一个,要求在这个范围内选择p。
显然,不可以只建一棵树,因为每加入一个点,就会更新当前点的信息,如果就不能确定 之间是否有满足条件的解了。
于是乎,可持久化闪亮登场了。
可持久化就是分别存储,,,,...,。
这样,给出了范围,就可以到第 r 棵树上进行匹配了,就不存在的情况了。
可持续化的建树过程如图所示:
下面是AC代码。
这道题调的让我有跳楼的冲动,最后是逐行debug,哈哈哈。
#include<bits/stdc++.h>
using namespace std;
#define deb cout<<"find:::"<<endl
const int MAXN=6e5+5;
int sum[MAXN];
int root[MAXN];
int tre[MAXN*25][2];
int flag[MAXN*25];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int t;
cin >> t;
sum[i] = sum[i - 1] ^ t;
}
flag[0]=-1;
int idx = 0;
function<void(int, int, int, int)>build = [&](int id, int pos, int fa, int now) {
// cout<<"asda::"<<id<<' '<<pos<<' '<<fa<<' '<<now<<endl;
if (pos < 0) {
flag[now] = id;
return;
}
int bit = sum[id] >> pos & 1;
if(fa) tre[now][bit ^ 1] = tre[fa][bit ^ 1];
tre[now][bit] = ++idx;
build(id, pos - 1, tre[fa][bit], tre[now][bit]);
flag[now] = flag[tre[now][bit]];
};
root[0]=++idx;
build(0, 23, 0, root[0]);
for (int i = 1; i <= n; i++) {
root[i] = ++idx;
build(i, 23, root[i - 1], root[i]);
}
function<int(int, int, int,int )>query = [&](int L, int now, int t,int k) {
if (k < 0) {
return t ^ sum[flag[now]];
}
int p = t >> k & 1;
if (flag[tre[now][p ^ 1]] >= L) {
return query(L, tre[now][p ^ 1], t, k - 1);
}
else return query(L, tre[now][p], t, k - 1);
};
for (int i = 1; i <= m; i++) {
// cout<<"asd"<<endl;
char op;
cin >> op;
if (op == 'A') {
int x;
cin >> x;
n++;
root[n] = ++idx;
sum[n] = sum[n - 1] ^ x;
build(n, 23, root[n - 1], root[n]);
}
else {
int l, r, x;
cin >> l >> r >> x;
cout << query(l-1, root[r-1], sum[n] ^ x,23) << '\n';
}
}
return 0;
}
关于代码:
可持久化是一种优化空间进行存储所有情况的一种数据结构。虽然已经极大程度的减少空间使用了,但其开辟的空间还是很大,所以,在写可持久化的题时,最好不要使用vector。一定要计算一下开的数组会不会爆MLE。
这题我因为没有初始化flag[0]=-1debug了三小时..........