Luogu3822 NOI2017 整数

9 篇文章 0 订阅

题面luogu3822

题意:有一个二进制整数初始为 0 0 0。两种操作:加 a ⋅ 2 b a \cdot 2^b a2b;询问二进制下第 k k k位的值。操作数 n ≤ 1 0 6 n \le 10^6 n106 ∣ a ∣ ≤ 1 0 9 |a| \le 10^9 a109 0 ≤ b , k ≤ 30 n 0 \le b, k \le 30n 0b,k30n

题解:对加和减(对应 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 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值