原题链接:D.XOR of Suffix Sums
题目大意:
给出一个最初为空的数组 a a a,同时再给出 q q q 次操作。
每次操作给出两个数字 t , v t,v t,v,表示先从数组末尾删除连续 t t t 个数字,在向末尾加入一个数字 v v v,保证 v v v 为非负整数。
操作完之后,回答对于所有后缀和的异或值为多少。
即假设当前数组长度为 n n n,且 i i i 的后缀和为 S u f i = a i + a i + 1 + . . . + a n Suf_{i}=a_{i} +a_{i+1}+...+a_{n} Sufi=ai+ai+1+...+an 。
那么此时询问的答案即 S u f 1 ⊕ S u f 2 ⊕ . . . ⊕ S u f n Suf_{1} \oplus Suf_{2} \oplus... \oplus Suf_{n} Suf1⊕Suf2⊕...⊕Sufn ,答案对 2 21 2^{21} 221 取模。
解题思路:
每次操作即删除末尾若干个整数,再加入一个整数,删除操作肯定是添加操作量级的,这个好做,考虑如何求得答案。
设前缀和数组为 S S S,那么 S u f i = S n − S i − 1 Suf_{i}= S_{n}-S_{i-1} Sufi=Sn−Si−1 。
那么问题就转化成了 ⊕ i = 1 n S n − S i − 1 \oplus_{i=1}^{n}S_{n}-S_{i-1} ⊕i=1nSn−Si−1 。
考虑异或最基础的求答案方法,某个位上 1 1 1 的总数为偶数则为 0 0 0,为奇数则为 1 1 1。
那么我们对 2 20 2^{20} 220 次方的每个位分别统计 1 1 1 的个数即可。
我们把问题拆成 2 0 , 2 1 , . . . , 2 20 2^{0},2^{1},...,2^{20} 20,21,...,220 然后分别考虑某一个位置上 0 / 1 0/1 0/1 的变化。
我们要知道有多少个
i
i
i 满足
S
u
f
i
Suf_{i}
Sufi 在第
k
k
k 位上有
1
1
1 ,
即我们要找到有多少个
−
S
i
−
1
+
S
n
m
o
d
2
k
+
1
≥
2
k
-S_{i-1}+S_{n} \mod 2^{k+1} \ge 2^{k}
−Si−1+Snmod2k+1≥2k 就行了(此时第
k
k
k 位上一定会是
1
1
1)。
那么问题就转化为找模意义下的 2 k − S n ≤ − S i − 1 ≤ ( 2 k − S n ) + 2 k − 1 2^{k}-S_{n} \leq -S_{i-1} \leq (2^{k}-S_{n}) + 2^{k}-1 2k−Sn≤−Si−1≤(2k−Sn)+2k−1 的 i i i 的数量就是有多少个 S n − S i − 1 S_{n}-S_{i-1} Sn−Si−1 第 k k k 位为 1 1 1 的 i i i 的数量(这里需要理解一下)。
注意这里的 l = 2 k − S n , r = ( 2 k − S n ) + 2 k − 1 l=2^{k}-S_{n},r=(2^{k} - S_{n}) + 2^{k}-1 l=2k−Sn,r=(2k−Sn)+2k−1 由于存在 m o d 2 k + 1 \mod 2^{k+1} mod2k+1 的原因可能存在 l > r l>r l>r,此时询问的细节要注意一下变成询问区间 [ 0 , r ] [0,r] [0,r] 和区间 [ l , 2 k + 1 − 1 ] [l,2^{k+1}-1] [l,2k+1−1]。
那么实现方面我们就是要查询值域 [ l , r ] [l,r] [l,r] 范围内的数字个数的和,还要支持加删改,显然这里树状数组是最方便的。
我们要对每个 k k k 分别维护一个树状数组,假设 V V V 为值域则需要 O ( log V ) O(\log V) O(logV) 棵树,每棵树询问的复杂度为 O ( log V ) O(\log V) O(logV) 那么单次操作的复杂度为 O ( log 2 V ) O(\log^{2} V) O(log2V) 可以通过。
细节挺多的,写的时候注意一些即可。
时间复杂度: O ( q log 2 V ) O(q \log^{2} V) O(qlog2V)
#include <bits/stdc++.h>
using i64 = long long;
template<typename T>
struct BIT {
const int n;
std::vector<T> tree;
BIT(int n) : n(n), tree(n + 1) {};
T Query(int x) {
T res = 0;
for (int i = x; i > 0; i -= (i & -i)) {
res += tree[i];
}
return res;
}
void Modify(int l, T z) {
if (l <= 0) return;
for (int i = l; i <= n; i += (i & -i)) {
tree[i] += z;
}
}
T rangeQuery(int l, int r) {
return Query(std::min(n, r)) - Query(std::max(0, l - 1));
}
};
void solve() {
int q;
std::cin >> q;
const int N = 21, mod = 1 << 21;
int ans = 0;
std::vector<int> A(1);
//这里我的树状数组区间为 [1, n] 查询时将 [0, n - 1] 变成 [1, n] 即可
std::vector BitCnt(N, BIT<int>(1 << N));
//k进制下的S[n]
std::vector<i64> S(N);
auto query = [&](int k) -> int {
const int v = 1 << k, m = 2 << k;
int l = ((v - S[k]) % m + m) % m;
l = (l == 0 ? m : l);
int r = ((l + v - 1) % m + m) % m;
r = (r == 0 ? m : r);
if (l <= r) {
return BitCnt[k].rangeQuery(l, r) % 2;
} else {
return (BitCnt[k].rangeQuery(1, r) % 2) ^ (BitCnt[k].rangeQuery(l, m) % 2);
}
};
//一定要注意add 和 del的区别
auto add = [&](int v) -> void {
A.emplace_back(v % mod);
for (int i = 0; i < N; ++i) {
ans ^= query(i) << i;
int m = (2 << i);
int x = (m - S[i]) % m;
x = (x == 0 ? m : x);
BitCnt[i].Modify(x, 1);
S[i] = (S[i] + A.back()) % m;
ans ^= query(i) << i;
}
};
auto del = [&]() -> void {
for (int i = 0; i < N; ++i) {
ans ^= query(i) << i;
int m = (2 << i);
S[i] = (S[i] - A.back() % m + m) % m;
int x = (m - S[i]) % m;
x = (x == 0 ? m : x);
BitCnt[i].Modify(x, -1);
ans ^= query(i) << i;
}
A.pop_back();
};
for (int i = 1; i <= q; ++i) {
int t, v;
std::cin >> t >> v;
for (int j = 0; j < t; ++j) {
del();
}
add(v);
std::cout << ans << "\n";
}
}
signed main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int t = 1;
//std::cin >> t;
while (t--) {
solve();
}
return 0;
}