题面:luogu3822
题意:有一个二进制整数初始为 0 0 0。两种操作:加 a ⋅ 2 b a \cdot 2^b a⋅2b;询问二进制下第 k k k位的值。操作数 n ≤ 1 0 6 n \le 10^6 n≤106, ∣ a ∣ ≤ 1 0 9 |a| \le 10^9 ∣a∣≤109, 0 ≤ b , k ≤ 30 n 0 \le b, k \le 30n 0≤b,k≤30n
题解:对加和减(对应 a a a的正负)分别维护二进制数,记做 i n c inc inc和 d e c dec dec。查询时需要比较从该位置开始的后缀的 i n c inc inc和 d e c dec dec的大小,判断是否需要退位。
直接存二进制高精度每次做加法需要 O ( log a ) O(\log a) O(loga)的复杂度。
考虑用unsigned int压32位一块。每次加法 O ( 1 ) O(1) O(1)。
因为需要比较后缀的大小,用一个set存所有 i n c inc inc和 d e c dec dec不同的块的位置即可。
#include <bits/stdc++.h>
typedef unsigned int uint ;
const int maxn = 1e6 + 10 ;
inline int read() {
int x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
uint inc[maxn], dec[maxn] ;
std::set<int> s ;
inline void add (uint a[], uint b[], int val, int bit) {
int p = bit / 32, q = bit % 32 ;
uint tmp = (uint)val << q, inc = (uint)val >> (31 - q); inc >>= 1 ;
uint pre = a[p] ;
a[p] += tmp; inc += (a[p] < pre) ;
if (a[p] ^ b[p]) s.insert (p) ;
else if (s.count (p)) s.erase (p) ;
p ++ ;
while (inc != 0) {
pre = a[p]; a[p] += inc; inc = (a[p] < pre) ;
if (a[p] ^ b[p]) s.insert (p) ;
else if (s.count (p)) s.erase (p) ;
p ++ ;
}
}
inline int query (int bit) {
int p = bit / 32, q = bit % 32 ;
int ans = ((inc[p] >> q) ^ (dec[p] >> q)) & 1 ;
uint inc_left = inc[p] % (1 << q), dec_left = dec[p] % (1 << q) ;
if (inc_left < dec_left) return ans ^ 1 ;
else if (inc_left > dec_left || s.empty() || p <= *(s.begin())) return ans ;
else {
std::set<int>::iterator it = s.lower_bound (p); -- it ;
if (inc[*it] > dec[*it]) return ans ;
else return ans ^ 1 ;
}
}
int main() {
int n = read(), t1 = read(), t2 = read(), t3 = read() ;
while (n --) {
int op = read() ;
if (op == 1) {
int a = read(), b = read() ;
if (a > 0) add (inc, dec, a, b) ;
else add (dec, inc, -a, b) ;
} else {
printf("%d\n", query (read())) ;
}
}
return 0 ;
}