BZOJ4942:[Noi2017]整数
? 解题记录 ? ? BZOJ ? ? 状态压缩 ? ? 线段树 ?    2018-01-16 07:35:28    577    0    0

【题目背景】
在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科
学家 P 博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,超级计算机
无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你. . . . . .
【题目描述】
P 博士将他的计算任务抽象为对一个整数的操作。
具体来说,有一个整数 x ,一开始为 0。
接下来有 n 个操作,每个操作都是以下两种类型中的一种:
1 a b :将 x 加上整数 a · 2
b,其中 a 为一个整数,b 为一个非负整数
2 k :询问 x 在用二进制表示时,位权为 2
k 的位的值(即这一位上的 1 代表 2
k )
保证在任何时候,x ≥ 0。
【输入格式】
从文件 integer.in 中读入数据。
输入的第一行包含四个正整数 n, t1, t2, t3,n 的含义见题目描述,t1, t2, t3 的具体含义
见子任务。
接下来 n 行,每行给出一个操作,具体格式和含义见题目描述。
同一行输入的相邻两个元素之间,用恰好一个空格隔开。
【输出格式】
输出到文件 integer.out 中。
对于每个询问操作,输出一行,表示该询问的答案(0 或 1)。对于加法操作,没有
任何输出。
【样例 1 输入】
10 3 1 2
1 100 0
1 2333 0
1 -233 0
2 5
2 7
2 15
1 5 15
2 15
1 -1 12
2 15
【样例 1 输出】
0
1
0
1
0
【样例 1 解释】
样例中有 10 个操作:第 1 个为将 x 加上 100 × 2
0 ,操作后,x = 100 ;
第 2 个为将 x 加上 2333 × 2
0 ,操作后,x = 2433 ;
第 3 个为将 x 加上 ?233 × 2
0 ,操作后,x = 2200 ;
第 4 个为询问 x 位权为 2
5 的位上的值,x 在二进制下为 100010011000 ,答案为 0 ;
第 5 个为询问 x 位权为 2
7 的位上的值,x 在二进制下为 100010011000 ,答案为 1 ;
第 6 个为询问 x 位权为 2
15 的位上的值,x 在二进制下为 100010011000 ,答案为
0 ;
第 7 个为将 x 加上 5 × 2
15 = 163840 ,操作后,x = 166040 ;
第 8 个为询问 x 位权为 2
15 的位上的值,x 在二进制下为 101000100010011000 ,答
案为 1 ;
第 9 个为将 x 加上 ?1 × 2
12 = ?4096 ,操作后,x = 161944 ;
第 10 个为询问 x 位权为 2
15 的位上的值,x 在二进制下为 100111100010011000 ,
答案为 0 。

首先把a*b拆分成log次单加操作。然后考虑对于一个位上的单加操作。如果不进位可以直接加,进位实际上是找到一串连续的1后的第一个0,并且把这一串1都变成0,将最后一个0变成1。可以用线段树优化。在线段树上维护两个bits信息,一个是否全0,一个是否全1。注意到这两个信息可以用位运算向上更新。复杂度O(n log^2 n)但是这样做对于100%的数据是会TLE的。发现线段树每一个节点只有0或1太浪费了。考虑状态压缩01串到一个32位整型中。这样复杂度是O(n log^2 n / 32),可以过了。

P.S.洛谷上被卡常了555……

#include<cstdio>
#include<cctype>
using namespace std;
const int block = 30;
const int maxn = 1e6 + 150, stmax = (1LL << block) - 1;
int n, a, b, N, opt, t1, t2, t3, f = 0;
 
inline char gc(){
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(int & x) {
    x = 0, f = 0;static char c = gc();
    while(!isdigit(c)) {
        if(c == '-') f = 1;
        c = gc();
    }
    while(isdigit(c)) x = x * 10 + c - '0', c = gc();
    if(f) x = -x;
}
namespace Segtree {
    struct node {int bits[2], edt;node() {edt = -1;}}tree[maxn << 2];
    void push_up(int rt) {
        tree[rt].bits[0] = tree[rt << 1].bits[0] | tree[rt << 1 | 1].bits[0];
        tree[rt].bits[1] = tree[rt << 1].bits[1] & tree[rt << 1 | 1].bits[1];
    }
    void push_down(int rt) {
        if(~tree[rt].edt) {
            tree[rt << 1].edt = tree[rt << 1].bits[0] = tree[rt << 1].bits[1] = tree[rt].edt;
            tree[rt << 1 | 1].edt = tree[rt << 1 | 1].bits[0] = tree[rt << 1 | 1].bits[1] = tree[rt].edt;
            tree[rt].edt = -1;
        }
    }
    int qNful(int p, int tl, int tr, int rt, int t, int des) {
        if(tree[rt].bits[t] == des) return -1;
        if(tl == tr) return tl;
        push_down(rt);
        int mid = tl + tr >> 1;
        if(p < mid) {
            int ret = qNful(p, tl, mid, rt << 1, t, des);
            return (~ret) ? ret : qNful(p, mid + 1, tr, rt << 1 | 1, t, des);
        }
        return qNful(p, mid + 1, tr, rt << 1 | 1, t, des);
    }
    void edit(int l, int r, int tl, int tr, int rt, int msk) {
        if(tl == l && tr == r) {
            tree[rt].edt = tree[rt].bits[0] = tree[rt].bits[1] = msk;
            return ;
        }
        push_down(rt);
        int mid = tl + tr >> 1;
        if(r <= mid) edit(l, r, tl, mid, rt << 1, msk);
        else if(l > mid) edit(l, r, mid + 1, tr, rt << 1 | 1, msk);
        else {
            edit(l, mid, tl, mid, rt << 1, msk);
            edit(mid + 1, r, mid + 1, tr, rt << 1 | 1, msk);
        }
        push_up(rt);
    }
    void add(int p, int tl, int tr, int rt, int dt) {
        if(tl == tr) {
            tree[rt].bits[0] = (tree[rt].bits[1] += dt);
            return ;
        }
        push_down(rt);
        int mid = tl + tr >> 1;
        if(p <= mid) add(p, tl, mid, rt << 1, dt);
        else add(p, mid + 1, tr, rt << 1 | 1, dt);
        push_up(rt);
    }
    int query(int p, int tl, int tr, int rt) {
        if(tl == tr) return tree[rt].bits[0];
        push_down(rt);
        int mid = tl + tr >> 1;
        if(p <= mid) return query(p, tl, mid, rt << 1);
        else return query(p, mid + 1, tr, rt << 1 | 1);
    }
    void plus(int p, int msk) {
        int now = query(p, 0, N, 1);
        if(now + msk <= stmax) add(p, 0, N, 1, msk);
        else {
            add(p, 0, N, 1, -(stmax - msk + 1)); // 减去前面的连续1 
            now = qNful(p, 0, N, 1, 1, stmax);
            if(p + 1 < now)edit(p + 1, now - 1, 0, N, 1, 0);
            add(now, 0, N, 1, 1);
        }
    }
    void minus(int p, int msk) {
        int now = query(p, 0, N, 1);
        if(now - msk >= 0) add(p, 0, N, 1, -msk);
        else {
            add(p, 0, N, 1, (stmax - msk + 1)); // 加上前面的连续1
            now = qNful(p, 0, N, 1, 0, 0);
            if(p + 1 < now)edit(p + 1, now - 1, 0, N, 1, stmax);
            add(now, 0, N, 1, -1);
        }
    }
}
inline void opr(int a, int b) {
    using namespace Segtree;
    if(!a) return ;
    int type = a > 0, pw = 0;
    if(!type) a = -a;
    while(a) {
        if(a & 1)
            if(type) plus((pw + b) / block, 1 << ((pw + b) % block));
            else minus((pw + b) / block, 1 << ((pw + b) % block));
        a >>= 1, ++pw;
    }
}
int main() {
    using namespace Segtree;
    scanf("%d%d%d%d", &n, &t1, &t2, &t3), N = 1e6 + 100;
    while(n--) {
        read(opt), read(a);
        if(opt == 1) {
            read(b);
            opr(a, b);
        } else {
            putchar((query(a / block, 0, N, 1) & (1 << (a % block))) ? '1' : '0');
            putchar('\n');
        }
    }
    return 0;
}

上一篇: 洛谷P2042 [NOI2005]维护数列

下一篇: 字符串

577 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航