CodeChef CookOff 2014 March XOR Minimization
問題
長さNの配列があり、次のようなクエリが来るので処理せよ。
1 l r : {a[i]|l≦i≦r}の最小値を求める。
2 l r x : l≦i≦rなるiについて、a[i] = a[i] ^ xとする。^はbitwise xor
制約条件
N≦250000
0≦a[i], x≦65535
方針
セグメント木を使おうとしたけど更新が上手くできなかった。
平方分割だと階層がないので上手くできた。
平方分割で、各バケットごとにTrie木と、その区間全体にかかっているxorを持つ。
Trie木にはそのバケットが担当する配列を2進法であらわした文字列について、それぞれ個数を入れておく。
で、最小値の求め方。
区間にすっぽり収まっているバケットについては、
Trieを使って最小値を求める。xorで1が立っているビットは1を優先して、立ってないビットは0を優先してTrie木を辿ろうとすれば、最小値がわかる。
区間にすっぽり収まっていないバケットについては、直接計算する。
更新の仕方は以下の通り。
区間にすっぽり収まっているバケットについては、xor値だけを更新する。
バケットからはみ出ている要素は、その要素の値を直接書き換える。
それとともにTrie木も更新する。
計算量は、O(Q * sqrt(N) * log(A))とか。
ソースコード
#define F first #define S second const int N = 250000; const int M = 500; int child[N*16*10][2], value[N*16*10], trie_sz = 1; inline int node(){ assert(trie_sz + 1 < N*16*10); return trie_sz++; } int n, q, a[N]; int root[M], sum[M]; pi mn[M]; inline void add(int &r, int v, int x, int pos = 15){ if(!r) r = node(); value[r] += x; if(pos < 0) return; add(child[r][v >> pos & 1], v, x, pos - 1); } inline pi get(int r, int s, int ans = 0, int pos = 15){ if(pos < 0) return mp(ans, value[r]); rep(ii, 2){ int i = ii ^ (s >> pos & 1); if(child[r][i] && value[child[r][i]]) return get(child[r][i], s, ans * 2 + ii, pos - 1); } assert(0); } int main(){ scanf("%d%d", &n, &q); rep(i, n) scanf("%d", a + i); rep(i, M) mn[i] = mp(inf, 0); rep(i, n){ add(root[i / M], a[i], 1); if(mn[i / M].F > a[i]) mn[i / M] = mp(a[i], 0); if(mn[i / M].F == a[i]) mn[i / M].S++; } //rep(i, n) cerr<<mn[i/M].F<<" "<<mn[i/M].S<<" : "<<i<<endl; while(q--){ int type, l, r, x; scanf("%d%d%d", &type, &l, &r); l--; if(type == 1){ pi ans = mp(inf, 0); while(l % M && l < r){ if(ans.F > (a[l] ^ sum[l / M])) ans = mp(a[l] ^ sum[l / M], 0); if(ans.F == (a[l] ^ sum[l / M])) ans.S++; l++; } while(r % M && l < r){ r--; if(ans.F > (a[r] ^ sum[r / M])) ans = mp(a[r] ^ sum[r / M], 0); if(ans.F == (a[r] ^ sum[r / M])) ans.S++; } l /= M; r /= M; while(l < r){ if(ans.F > mn[l].F) ans = mp(mn[l].F, 0); if(ans.F == mn[l].F) ans.S += mn[l].S; l++; } printf("%d %d\n", ans.F, ans.S); } else{ scanf("%d", &x); while(l % M && l < r){ add(root[l / M], a[l], -1); a[l] ^= x; add(root[l / M], a[l], 1); l++; if(l % M == 0) mn[l / M - 1] = get(root[l / M - 1], sum[l / M - 1]); } while(r % M && l < r){ r--; add(root[r / M], a[r], -1); a[r] ^= x; add(root[r / M], a[r], 1); if(r % M == 0) mn[r / M] = get(root[r / M], sum[r / M]); } l /= M; r /= M; while(l < r){ sum[l] ^= x; mn[l] = get(root[l], sum[l]); l++; } } } return 0; }