BZOJ3261 最大异或和 可持久化trie

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));
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轩辕青山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值