3261: 最大异或和
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 4291 Solved: 1800
[Submit][Status][Discuss]
Description
给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。
Input
第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。
Output
假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。
Sample Input
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
对于测试点 1-2,N,M<=5 。
对于测试点 3-7,N,M<=80000 。
对于测试点 8-10,N,M<=300000 。
其中测试点 1, 3, 5, 7, 9保证没有修改操作。
0<=a[i]<=10^7。
Sample Output
4
5
6
正解思路:
设s[i]表示序列a的前i个数xor起来的得到的结果。特别地,设s[0]=0.根据异或运算的性质:
a[p] xor a[p+1] xor … xor a[N] xor x = s[p-1] xor s[N] xor x
对于“询问操作”,问题变为:已知一个整数val = s[N] xor x,求一个位置p ,满足
l-1≤p≤r-1,是的s[p] xor val 最大。
只需把序列s的每个数看作二进制数,以字符串的形式插入trie,询问时在trie中优先尝试与val的每一位相反的指针即可。
要满足l-1≤p≤r-1,我们可以维护一个可持久化Trie,并在每个节点上增加一些信息,设end[x]表示可持久化Trie的节点x是序列s中第几个数的末尾节点,(若不是末尾节点,end[x]=-1)设latest[x] 表示在可持久化Trie以x为根的子树中end的最大值。从root[r-1] 出发检索 val 时, 仅考虑latest值不小于l-1的节点即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N = 600010;
int trie[N * 24][2], latest[N * 24]; // latest和end可合并为一个数组
int s[N], root[N], n, m, tot;
// 本题需要统计子树latest,故使用递归插入
// 插入s[i],当前为s[i]的第k位
void insert(int i, int k, int p, int q) {
if (k < 0) {
latest[q] = i;
return;
}
int c = s[i] >> k & 1;
if (p) trie[q][c ^ 1] = trie[p][c ^ 1];
trie[q][c] = ++tot;
insert(i, k - 1, trie[p][c], trie[q][c]);
latest[q] = max(latest[trie[q][0]], latest[trie[q][1]]);
}
int ask(int now, int val, int k, int limit) {
if (k < 0) return s[latest[now]] ^ val;
int c = val >> k & 1;
if (latest[trie[now][c ^ 1]] >= limit)
return ask(trie[now][c ^ 1], val, k - 1, limit);
else
int main() {
cin >> n >> m;
latest[0] = -1;
root[0] = ++tot;
insert(0, 23, 0, root[0]);
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
s[i] = s[i - 1] ^ x;
root[i] = ++tot;
insert(i, 23, root[i - 1], root[i]);
}
for (int i = 1; i <= m; i++) {
char op[2]; scanf("%s", op);
if (op[0] == 'A') {
int x; scanf("%d", &x);
root[++n] = ++tot;
s[n] = s[n - 1] ^ x;
insert(n, 23, root[n - 1], root[n]);
}
else {
int l, r, x; scanf("%d%d%d", &l, &r, &x);
printf("%d\n", ask(root[r - 1], x ^ s[n], 23, l - 1));
}
}
}
return ask(trie[now][c], val, k - 1, limit);
}
int main() {
cin >> n >> m;
latest[0] = -1;
root[0] = ++tot;
insert(0, 23, 0, root[0]);
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
s[i] = s[i - 1] ^ x;
root[i] = ++tot;
insert(i, 23, root[i - 1], root[i]);
}
for (int i = 1; i <= m; i++) {
char op[2]; scanf("%s", op);
if (op[0] == 'A') {
int x; scanf("%d", &x);
root[++n] = ++tot;
s[n] = s[n - 1] ^ x;
insert(n, 23, root[n - 1], root[n]);
}
else {
int l, r, x; scanf("%d%d%d", &l, &r, &x);
printf("%d\n", ask(root[r - 1], x ^ s[n], 23, l - 1));
}
}
}