0
点赞
收藏
分享

微信扫一扫

Luogu P4735 最大异或和


题目链接:​​传送门​​​ 支持在序列末尾添一个数,查询区间内一个位置使这个位置到序列末尾和给定数x的异或值最大

可持久化01trie
就是每个节点存数字的个数
从高位到低位贪心
区间中一个位置到n的异或值与x异或最大
就是n的异或前缀和与x异或后的值与区间内任意前缀和异或最大
所以将n的异或前缀和与x异或的值放在01trie中贪心找反位就可以了
这样一化简
题面里的式子就是s[p-1] ^ s[n] ^ x
是异或前缀和
没有故意压行
就删了那么俩回车
真的
直接这样会T一个点
要不开O2要不加快读

#include <cstdio>
#define

int s[A], ch[A][2], cnt, rt[A], n, m, a, b, c, sum;
void build(int pre, int &k, int val, int pos) {
k = ++cnt, s[k] = s[pre] + 1;
if (pos < 0) return; int iw = (val >> pos) & 1;
ch[k][iw ^ 1] = ch[pre][iw ^ 1];
build(ch[pre][iw], ch[k][iw], val, pos - 1);
}
int ask(int pre, int k, int val, int pos) {
if (pos < 0) return 0; int iw = (val >> pos) & 1;
if (s[ch[k][iw ^ 1]] - s[ch[pre][iw ^ 1]] > 0) return (1 << pos) + ask(ch[pre][iw ^ 1], ch[k][iw ^ 1], val, pos - 1);
else return ask(ch[pre][iw], ch[k][iw], val, pos - 1);
}

int main(int argc, char const *argv[]) {
scanf("%d%d", &n, &m); build(rt[0], rt[1], 0, 24); n++;
for (int i = 2; i <= n; i++) scanf("%d", &a), sum ^= a, build(rt[i - 1], rt[i], sum, 24);
while (m--) {
char opt[1]; scanf("%s", opt);
if (opt[0] == 'A') scanf("%d", &a), sum ^= a, build(rt[n], rt[n + 1], sum, 24), n++;
else scanf("%d%d%d", &a, &b, &c), printf("%d\n", ask(rt[a - 1], rt[b], c ^ sum, 24));
}
}


举报

相关推荐

0 条评论