可持久化Trie樹和可持久化線段樹很像,依次插入信息,通過減法來進行歷史版本查詢。
2015年11月27日
bzoj3261 最大異或和
我們需要計算 a[p] xor a[p+1] xor ... xor a[N] xor x ,設 sum[i] 表示 a[1] xor a[2] xor ... xor a[i] 的值,因為異或滿足區間減法,所以求上一個式子等於求 sum[n] xor sum[p - 1] xor x,進一步,sum[n] xor x 為定值,所以需要找到二進制位上盡量不匹配的,由此需要使用Trie樹。
同時題目中有區間限制,所以我們需要一個可持久化的數據結構。
1) 順次將sum[i]插入到可持久化Trie樹中。注意 : 我們需要讓數位對齊,否則高位低位會錯位,所以需要在每一個數的前面加上適當的0以使他們數位相等。
插入時,可以采取遞歸的方式,用 >> 來確定這一位是多少,把沒有改變的那一邊鏈向歷史版本。注意 : 在遞歸時,若采取d--(此處見代碼)的方式,因為一個節點的左右兒子不是在同一個遞歸中構造完成,所以判斷 d < 0 ---> break; 需要放在新建節點之后。
2) 對於查詢時,從根節點開始,同樣采用d--的方式,逐位確定,如果 x 的這一位為 p ,那么我們查詢Sum[son[l][p ^ 1]] - Sum[son[r][p ^ 1],Sum為節點上有多少的值,如若 表達式 > 0 那么我們就像 p ^ 1 的方向行走,同時 答案加上 1 << d 因為這一位被我們錯開了,否則只好向 p 的方向行走, 不加上 1 << d。
3) 因為我們要計算的是 sum[p - 1] xor sum[n] xor[x] 的值,所以對於給定的p的限制區間,我們必須把區間減一,再把左端點減一后查詢,這里有一個小技巧 : 首先插入一個 0 的值到可持久化Trie樹中,以作為第一個節點,對於后面的讀入 比如說(l, r) 直接查詢 (l - 1, r) 即可,因為已經事先減一了。
4) 對於空間的問題,因為一條鏈最多有 floor(log(1e7)) + 1 的長度,又因為 n <= 300000, m <= 300000,有可能所有的option均為A,所以 (Maxn + Maxm) * (floor(log(1e7)) + 1)
1 #include <bits/stdc++.h> 2 #define rep(i, a, b) for (int i = a; i <= b; i++) 3 #define REP(i, a, b) for (int i = a; i < b; i++) 4 #define drep(i, a, b) for (int i = a; i >= b; i--) 5 #define pb push_back 6 #define mp make_pair 7 #define xx first 8 #define yy second 9 using namespace std; 10 typedef long long i64; 11 typedef pair<int, int> pii; 12 const int inf = ~0U >> 1; 13 const i64 INF = ~0ULL >> 1; 14 //***************************** 15 const int maxn = 300005, maxm = 14400005; 16 17 int Sum[maxm], Son[maxm][2], root[maxm], ndtot; 18 19 void build(int x, int &y, int v, int d) { 20 Sum[y = ++ndtot] = Sum[x] + 1; 21 if (d < 0) return; 22 int p = v >> d & 1; 23 Son[y][p ^ 1] = Son[x][p ^ 1]; 24 build(Son[x][p], Son[y][p], v, d - 1); 25 } 26 27 int tot; 28 29 int query(int x, int y, int v, int d) { 30 if (d < 0) return 0; 31 int p = v >> d & 1; int tmp = Sum[Son[y][p ^ 1]] - Sum[Son[x][p ^ 1]]; 32 if (tmp > 0) return (1 << d) + query(Son[x][p ^ 1], Son[y][p ^ 1], v, d - 1); 33 else return query(Son[x][p], Son[y][p], v, d - 1); 34 } 35 36 int main() { 37 int n, m; 38 scanf("%d%d", &n, &m); 39 build(root[0], root[1], 0, 24), n++; 40 rep(i, 2, n) { 41 int id; 42 scanf("%d", &id); 43 tot ^= id; 44 build(root[i - 1], root[i], tot, 24); 45 } 46 char op[5]; 47 while (m--) { 48 scanf("%s", op); 49 if (op[0] == 'A') { 50 int id; 51 scanf("%d", &id); 52 tot ^= id; 53 build(root[n], root[n + 1], tot, 24); 54 n++; 55 } 56 else { 57 int x, y, k; 58 scanf("%d%d%d", &x, &y, &k); 59 printf("%d\n", query(root[x - 1], root[y], tot ^ k, 24)); 60 } 61 } 62 }
