题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6579
题目大意是两个操作,1个是求【l,r】区间子序列的最大异或和,另一个是在最后面添加一个数。
如果题目简化成求【1,,i】的最大异或和,那么该怎么想呢....
当然是处理出来一个前缀线性基啦。那么如何求一个区间呢....
那就处理前缀线性基的时候记录线性基上该位置是在原序列的哪个位置,求值的时候在第r个线性基上找,如果位置>=l则是合法的。
可是这样想有点怪怪的。
如果有两个相同的数在同一位那不就不好说了嘛?
遇到这种情况则用贪心的想法,如果在同一位上明显原序列越大的数越优,因为如果把原序列小的留下,则区间长度较小时明显没有原序列大的数更优,所以贪心的留下原序列大的。
1 #include <algorithm> 2 #include<iostream> 3 #include <cstdio> 4 #include <vector> 5 #include <cstring> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int maxn = 5e5 + 3; 10 int p[maxn][40], pos[maxn][40]; 11 int a[maxn]; 12 void insert(int k, int x) { 13 for (int i = 0; i <= 30; i++) { 14 p[k][i] = p[k - 1][i]; 15 pos[k][i] = pos[k - 1][i]; 16 } 17 int w = k; 18 for (int i = 30; i >= 0; i--) { 19 if (x&(1 << i)) { 20 if (p[k][i] == 0) { 21 p[k][i] = x; 22 pos[k][i] = w; 23 break; 24 } 25 else { 26 if (w > pos[k][i]) { 27 swap(pos[k][i], w); 28 swap(p[k][i], x); 29 } 30 x ^= p[k][i]; 31 } 32 } 33 } 34 } 35 int main() { 36 int t; 37 scanf("%d", &t); 38 while (t--) { 39 int n, m; 40 int ans = 0; 41 scanf("%d%d", &n, &m); 42 for (int i = 1; i <= n; i++) { 43 scanf("%d", &a[i]); 44 insert(i, a[i]); 45 } 46 for (int i = 1; i <= m; i++) { 47 int opt, x, y; 48 scanf("%d", &opt); 49 if (opt) { 50 scanf("%d", &x); 51 a[++n] = x ^ ans; 52 insert(n, a[n]); 53 } 54 else { 55 scanf("%d%d", &x, &y); 56 int l = (x^ans) % n + 1, r = (y^ans) % n + 1; 57 if (l > r) 58 swap(l, r); 59 ans = 0; 60 for (int j = 30; j >= 0; j--) 61 if ((ans^p[r][j]) > ans && pos[r][j] >= l)ans ^= p[r][j]; 62 printf("%d ", ans); 63 } 64 65 } 66 } 67 }