链接:https://www.luogu.org/problemnew/show/P4735
题目描述
给定一个非负整数序列 {a}{a} ,初始长度为 NN 。
有M个操作,有以下两种操作类型:
A x
:添加操作,表示在序列末尾添加一个数 xx ,序列的长度 N+1N+1 。Q l r x
:询问操作,你需要找到一个位置 pp ,满足 l le p le rl≤p≤r ,使得: a[p] oplus a[p+1] oplus ... oplus a[N] oplus xa[p]⊕a[p+1]⊕...⊕a[N]⊕x 最大,输出最大是多少。
输入输出格式
输入格式:
第一行包含两个整数 N,MN,M ,含义如问题描述所示。
第二行包含 NN 个非负整数,表示初始的序列 AA 。
接下来 MM 行,每行描述一个操作,格式如题面所述。
输出格式:
假设询问操作有 TT 个,则输出应该有 TT 行,每行一个整数表示询问的答案。
输入输出样例
说明
对于测试点 1-21−2 , N,M le 5N,M≤5 。
对于测试点 3-73−7 , N,M le 80000N,M≤80000 。
对于测试点 8-108−10 , N,M le 300000N,M≤300000 。
其中测试点 1, 3, 5, 7, 91,3,5,7,9 保证没有修改操作。
0 le a[i] le 10^70≤a[i]≤107 。
题解:
先看简单版:
给出 n 个数,问最大连续异或和。(n<=100000 , num<=2^31-1)
经典贪心。再利用上异或的性质,即 b ^ a ^ b =a 。记录前缀异或和,即转化为找两个数使得其异或和最大,同前。O(n*位数)
我们先来考虑,若没有添加操作,显然可以将所有的后缀异或和建成可持久化Trie树来做。那如果添加了k个元素呢?
查询时可以将x异或上添加的k个元素,再到Trie中查询。
建树 O(N * 位数) ;单次查询 O(k + 位数)
注意边界,开始应该把数组整体右移,不然查询1的时候会出问题
#include <bits/stdc++.h> #define ll long long using namespace std; const int M = 600000 + 5, M_trie = M * 30; int root[M], a[M], bin[33], tot; struct Trie{ struct node{ int cnt[2]; int ptr[2]; }tree[M_trie]; int insert(int num, int f){ int res = ++tot; int x = num, cur = res; tree[res] = tree[f]; for(int i = 27; i; i--){ int t = x & bin[i - 1]; t >>= (i - 1); int nw = ++tot; tree[cur].cnt[t]++; tree[cur].ptr[t] = nw; f = tree[f].ptr[t]; tree[nw] = tree[f]; cur = tree[cur].ptr[t]; } return res; } int query(int lf, int rg, int num){ int ans = 0; for(int i = 27; i; i--){ int t = num & bin[i - 1]; t >>= (i - 1); if(tree[rg].cnt[t^1] - tree[lf].cnt[t^1]){ lf = tree[lf].ptr[t^1], rg = tree[rg].ptr[t^1]; ans += bin[i - 1]; } else { lf = tree[lf].ptr[t], rg = tree[rg].ptr[t]; } } return ans; } }Tr; int query(int lf, int rg, int num){ return Tr.query(root[lf - 1], root[rg], num); } int main(){ // freopen("data.in","r",stdin); // freopen("mydata.out","w",stdout); bin[0] = 1; for(int i = 1; i <= 30; i++)bin[i] = bin[i - 1] << 1; int n, q; scanf("%d%d", &n, &q); ++n; root[1] = Tr.insert(0, root[0]); for(int i = 2; i <= n; i++){ scanf("%d", &a[i]); a[i] ^= a[i - 1]; root[i] = Tr.insert(a[i], root[i - 1]); } int cnt = n; char s[5]; int x, l, r; for(int i = 1; i <= q; i++){ scanf("%s", s); if(s[0] == 'Q'){ scanf("%d%d%d", &l, &r, &x); int num = a[cnt] ^ x; // printf("QUERY:%d %d %d ", a[l-1], a[r], num); printf("%d ", query(l, r, num)); } else { scanf("%d", &x); a[++cnt] = a[cnt - 1]^x; root[cnt] = Tr.insert(a[cnt], root[cnt - 1]); // printf("ADD:%d ", a[cnt]); } } }