[NOI 2017]整数

Description

题库链接

P 博士将他的计算任务抽象为对一个整数的操作。

具体来说,有一个整数 \(x\) ,一开始为 \(0\)

接下来有 \(n\) 个操作,每个操作都是以下两种类型中的一种:

  • 1 a b :将 \(x\) 加上整数 \(a\cdot 2^b\) ,其中 \(a\) 为一个整数, \(b\) 为一个非负整数
  • 2 k :询问 \(x\) 在用二进制表示时,位权为 \(2^k\) 的位的值(即这一位上的 \(1\) 代表 \(2^k\)

保证在任何时候, \(x\geqslant 0\)

\(1\leq n\leq 10^6,|a| \leq 10^9,0 \leq b, k \leq 30n\)

Solution

考虑稍微暴力一点的做法,我们开两个数组来模拟进位(一个是 \(a > 0\) ,另一个 \(a < 0\) )。

然后对于询问,我们假设小于 \(k\) 位的部分 \(a>0\) 的是 \(s_1\)\(a<0\) 的是 \(s_2\)

讨论所有情况,我们可以得出结论:

  • \(s_1\geq s2\) ,输出答案为 \([x\oplus y]\) ,其中 \(x\)\(a>0\) 的第 \(k\) 位的值, \(y\)\(a<0\) 的第 \(k\) 位的值。
  • \(s_1< s2\) ,输出答案为 \([x=y]\)

这样总复杂度是 \(O(30n\log(30n))\) 的。

考虑优化。

直接拿 \(\text{zkw线段树}\) 卡过去啦!

Code

#include <bits/stdc++.h>
using namespace std;
const int M = 1000000*30+300;
void gi(int &x) {
    char ch = getchar(); x = 0; int flag = 0;
    for (; ch < '0' || ch > '9'; ch = getchar()) flag |= (ch == '-');
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x<<1)+(x<<3)+ch-48;
    if (flag) x = -x;
}

int s1[M], s2[M], tr[(1<<26)+5];
int n, N, rbsc, opt, a, b, lst[32], bin[32], tot;

void modify(int *s, int a, int b) {
    tot = 0;
    for (int i = 30; i >= 0 && a; i--)
        if (bin[i]&a) lst[++tot] = i, a -= bin[i];
    int r = lst[1]+b, l = lst[tot]+b;
    for (int i = 1; i <= tot; i++) {
        int loc = lst[i]+b;
        while (s[loc]) s[loc++] = 0;
        r = max(r, loc), s[loc] = 1;
    }
    for (int i = l; i <= r; i++) tr[N+i] = (s1[i]^s2[i]);
    for (l = (l+N)>>1, r = (r+N)>>1; l; l >>= 1, r >>= 1)
        for (int j = l; j <= r; j++) tr[j] = tr[j<<1]|tr[j<<1|1];
}
int query(int a) {
    for (a += N; a; a >>= 1)
        if (a&1&tr[a^1]) {
            for (a ^= 1; a < N; a = a<<1|tr[a<<1|1]);
            return a-N;
        }
    return -1;
}
void work() {
    gi(n); gi(rbsc), gi(rbsc), gi(rbsc);
    for (N = 1; N <= n*30; N <<= 1);
    bin[0] = 1; for (int i = 1; i <= 30; i++) bin[i] = (bin[i-1]<<1);
    while (n--) {
        gi(opt);
        if (opt == 1) {
            gi(a), gi(b);
            if (a < 0) modify(s2, -a, b);
            else modify(s1, a, b);
        }else {
            gi(a); int now = query(a);
            if (now == -1 || s1[now] > s2[now]) putchar('0'+(s1[a]^s2[a]));
            else putchar('0'+(s1[a] == s2[a]));
            putchar('\n');
        }
    }
}
int main() {work(); return 0; }

转载于:https://www.cnblogs.com/NaVi-Awson/p/9290018.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值