CF703D Mishka and Interesting sum
阿新 • • 發佈:2020-10-20
知識點:線段樹
原題面:Codeforces
題意簡述
給定一長度為 \(n\) 的數列 \(a\),給定 \(m\) 次詢問。
每次詢問給定區間內,出現過偶數次的權值 的異或和。
\(1\le n,m\le 10^6\),\(1\le a_i\le 10^9\)。
分析題意
根據異或的自反性,一個區間內所有數的異或和,為區間內出現過 奇數次 的權值的異或和。
再根據異或的自反性,一個區間的答案,即為區間出現過的權值的異或和,異或上區間內出現過 奇數次 的權值的異或和。
即有下式:
\[ans (l,r) = \left(\bigoplus_{i=l}^{r} a_i\right) \oplus \left(\bigoplus_{k\in \{a_{i}\}} k\right) \]顯然區間內所有數的異或和可字首和維護。
考慮如何求得區間內出現過的權值的異或和。
維護的資訊不便於刪除,套路地考慮掃描線。
令線段樹下標為 \(r\) 的葉節點維護區間 \([l,r]\) 的資訊。
其中 \(l\) 為當前掃描到的左端點。
維護 \(nex_{i}\),表示 \(a_i\) 右側等於 \(a_i\) 的第一個數,若不存在則為 \(n+1\)。
則對於一個位置 \(i\),其影響的範圍為 \([i,nex_i)\),他的出現令該區間權值的異或和 \(\oplus a_i\)。
初始 \(l=1\) 時,按上述方法將所有位置插入線段樹中,構造出初始狀態。
當 \(l+1\) 時,刪去了 \(a_{l-1}\)
在 \(l\) 右移的同時回答詢問。
線段樹支援區間異或即可。
單次 \(l+1\),單次查詢的複雜度均為 \(O(\log n)\)。
總複雜度 \(O((n+m)\log n)\)。
爆零小技巧
注意修改區間的開閉情況。
程式碼實現
//知識點:線段樹 /* By:Luckyblock */ #include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #include <map> #define LL long long const int kMaxn = 1e6 + 10; //============================================================= struct Query { int l, r, id; } q[kMaxn]; int n, m, a[kMaxn], nex[kMaxn], sum[kMaxn], ans[kMaxn]; std::map <int, int> pos; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Chkmax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void Chkmin(int &fir_, int sec_) { if (sec_ < fir_) fir_ = sec_; } bool CompareQuery(Query fir, Query sec) { if (fir.l != sec.l) return fir.l < sec.l; return fir.r < sec.r; } namespace Seg { #define ls (now_<<1) #define rs (now_<<1|1) #define mid ((L_+R_)>>1) int sum[kMaxn << 2], tag[kMaxn << 2]; void Pushdown(int now_) { if (! tag[now_]) return ; sum[ls] ^= tag[now_]; tag[ls] ^= tag[now_]; sum[rs] ^= tag[now_]; tag[rs] ^= tag[now_]; tag[now_] = 0; } void Modify(int now_, int L_, int R_, int l_, int r_, int val_) { if (l_ <= L_ && R_ <= r_) { sum[now_] ^= val_; tag[now_] ^= val_; return ; } Pushdown(now_); if (l_ <= mid) Modify(ls, L_, mid, l_, r_, val_); if (r_ > mid) Modify(rs, mid + 1, R_, l_, r_, val_); } int Query(int now_, int L_, int R_, int pos_) { if (L_ == R_) return sum[now_]; Pushdown(now_); if (pos_ <= mid) return Query(ls, L_, mid, pos_); return Query(rs, mid + 1, R_, pos_); } void Debug(int now_, int L_, int R_) { if (L_ == R_) { printf("%d ", sum[now_]); return ; } Pushdown(now_); Debug(ls, L_, mid); Debug(rs, mid + 1, R_); } #undef ls #undef rs #undef mid } void Prepare() { n = read(); for (int i = 1; i <= n; ++ i) { a[i] = read(); sum[i] = sum[i - 1] ^ a[i]; } for (int i = n; i >= 1; -- i) { if (pos.count(a[i])) { nex[i] = pos[a[i]]; } else { nex[i] = n + 1; } pos[a[i]] = i; } for (int i = 1; i <= n; ++ i) { Seg::Modify(1, 1, n, i, nex[i] - 1, a[i]); } m = read(); for (int i = 1; i <= m; ++ i) { q[i] = (Query) {read(), read(), i}; } std::sort(q + 1, q + m + 1, CompareQuery); } //============================================================= int main() { Prepare(); int i = 1; for (; i <= m; ++ i) { if (q[i].l > 1) break; ans[q[i].id] = sum[q[i].r] ^ sum[q[i].l - 1] ^ Seg::Query(1, 1, n, q[i].r); } for (int l = 2; l <= n; ++ l) { Seg::Modify(1, 1, n, l, nex[l - 1] - 1, a[l - 1]); for (; i <= m; ++ i) { if (q[i].l > l) break; ans[q[i].id] = sum[q[i].r] ^ sum[q[i].l - 1] ^ Seg::Query(1, 1, n, q[i].r); } } for (int i = 1; i <= m; ++ i) { printf("%d\n", ans[i]); } return 0; }