首页 > 其他 > 详细

【洛谷3822】[NOI2017] 整数(线段树压位).md

时间:2019-06-09 15:39:53      阅读:94      评论:0      收藏:0      [点我收藏+]

题目:

洛谷 3822

分析:

直接按题意模拟,完了。

将每次加 / 减拆成不超过 \(32\) 个对单独一位的加 / 减。

考虑给一个二进制位(下称「当前位」)加 \(1\) 时,如果这一位本来就是 \(0\) ,那么直接变成 \(1\) 。否则要考虑进位:向左(以后默认从右向左为低位至高位,与书写顺序相同)找到第一个为 \(0\) 的位 \(p\) ,将其变成 \(1\) ,并把从 \(p\) 到当前位中间所有的 \(1\) 变成 \(0\)

减法是类似的。退位操作就是向左找到第一个 \(1\) ,将其变成 \(0\) ,并把中间所有 \(0\) 变成 \(1\)

以上找第一个 \(1\) 或者 \(0\) 和区间修改均可用线段树完成,只需要维护每个结点对应的区间是否全 \(0\) 或全 \(1\) 即可。

但是将一个询问拆成 \(32\) 次常数太大,\(3.2\times 10^7\) 次修改再带上线段树的 \(\log 3\times 10^7\) 根本过不去。考虑压位,线段树每个叶子表示连续多个(我的代码中使用的是 \(60\) 个)二进制位,找第一个 \(1\) / \(0\) 改为找第一个非 \(0\) (全 \(0\) ) / 非 \(2^{60}\) (全 \(1\) )的数。这样,每次修改只需要拆成最多对两个位置的加 / 减。复杂度 \(O(n\log m)\) 其中 \(m\) 是最大位数。

代码:

注意线段树上二分找第一个非全 \(0\) / 非全 \(1\) 的数的做法。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    typedef unsigned long long ull;
    const int N = 1e6 + 10, DIGIT = 60;
    const ull BASE = 1ULL << DIGIT;
    namespace Segment_Tree
    {
        struct node
        {
            ull val;
            bool all0, all1, tag0, tag1;
        }tree[N << 2];
        void cov0(const int rot)
        {
            tree[rot].val = 0;
            tree[rot].all0 = tree[rot].tag0 = true;
            tree[rot].all1 = tree[rot].tag1 = false;
        }
        void cov1(const int rot)
        {
            tree[rot].val = BASE - 1ULL;
            tree[rot].all1 = tree[rot].tag1 = true;
            tree[rot].all0 = tree[rot].tag0 = false;
        }
        void update(const int rot)
        {
            tree[rot].all0 = (tree[rot << 1].all0 && tree[rot << 1 | 1].all0);
            tree[rot].all1 = (tree[rot << 1].all1 && tree[rot << 1 | 1].all1);
        }
        void pushdown(const int rot)
        {
            if (tree[rot].tag0)
            {
                cov0(rot << 1), cov0(rot << 1 | 1);
                tree[rot].tag0 = false;
            }
            else if (tree[rot].tag1)
            {
                cov1(rot << 1), cov1(rot << 1 | 1);
                tree[rot].tag1 = false;
            }
        }
        void cover0(const int rot, const int lt, const int rt, const int ls, const int rs)
        {
            if (ls <= lt && rt <= rs)
            {
                cov0(rot);
                return;
            }
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (ls <= mid)
                cover0(rot << 1, lt, mid, ls, rs);
            if (rs > mid)
                cover0(rot << 1 | 1, mid + 1, rt, ls, rs);
            update(rot);
        }
        void cover1(const int rot, const int lt, const int rt, const int ls, const int rs)
        {
            if (ls <= lt && rt <= rs)
            {
                cov1(rot);
                return;
            }
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (ls <= mid)
                cover1(rot << 1, lt, mid, ls, rs);
            if (rs > mid)
                cover1(rot << 1 | 1, mid + 1, rt, ls, rs);
            update(rot);
        }
        void change(const int rot, const int lt, const int rt, const int pos, const ull x)
        {
            if (pos > rt)
                return;
            if (lt == rt)
            {
                tree[rot].val = x;
                tree[rot].all0 = (x == 0);
                tree[rot].all1 = (x == (BASE - 1ULL));
                return;
            }
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (pos <= mid)
                change(rot << 1, lt, mid, pos, x);
            else
                change(rot << 1 | 1, mid + 1, rt, pos, x);
            update(rot);
        }
        ull query(const int rot, const int lt, const int rt, const int pos)
        {
            if (lt == rt)
                return tree[rot].val;
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (pos <= mid)
                return query(rot << 1, lt, mid, pos);
            else
                return query(rot << 1 | 1, mid + 1, rt, pos);
        }
        int find0(const int rot, const int lt, const int rt, const int pos)
        {
            if (lt == rt)
                return lt;
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (pos <= mid && !tree[rot << 1].all1)
            {
                int ans = find0(rot << 1, lt, mid, pos);
                if (ans <= N)
                    return ans;
            }
            if (!tree[rot << 1 | 1].all1)
                return find0(rot << 1 | 1, mid + 1, rt, pos);
            else
                return N + 1;
        }
        int find1(const int rot, const int lt, const int rt, const int pos)
        {
            if (lt == rt)
                return lt;
            int mid = (lt + rt) >> 1;
            pushdown(rot);
            if (pos <= mid && !tree[rot << 1].all0)
            {
                int ans = find1(rot << 1, lt, mid, pos);
                if (ans <= N)
                    return ans;
            }
            if (!tree[rot << 1 | 1].all0)
                return find1(rot << 1 | 1, mid + 1, rt, pos);
            else
                return N + 1;
        }
        void init()
        {
            cov0(1);
        }
    }
    ull extract(const ull a, const int l, const int r)
    {
        return (a & ((1ULL << r) - 1ULL)) >> l;
    }
    bool check(const ull a, const int p)
    {
        return a & (1ULL << p);
    }
    void add(const ull a, const int p)
    {
        using namespace Segment_Tree;
        ull now = query(1, 0, N, p);
        if (now + a >= BASE)
        {
            int pos = find0(1, 0, N, p + 1);
            ull tmp = query(1, 0, N, pos);
            change(1, 0, N, pos, tmp + 1ULL);
            if (pos > p + 1)
                cover0(1, 0, N, p + 1, pos - 1);
        }
        change(1, 0, N, p, (now + a) % BASE);
    }
    void sub(const ull a, const int p)
    {
        using namespace Segment_Tree;
        ull now = query(1, 0, N, p);
        if (now < a)
        {
            int pos = find1(1, 0, N, p + 1);
            ull tmp = query(1, 0, N, pos);
            change(1, 0, N, pos, tmp - 1ULL);
            if (pos > p + 1)
                cover1(1, 0, N, p + 1, pos - 1);
        }
        change(1, 0, N, p, (now - a + BASE) % BASE);
    }
    int work()
    {
        using namespace Segment_Tree;
        int n, t1, t2, t3;
        read(n), read(t1), read(t2), read(t3);
        init();
        while (n--)
        {
            int opt;
            read(opt);
            if (opt == 1)
            {
                int a, b;
                read(a), read(b);
                if (a > 0) //ADD
                {
                    add(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
                    add(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
                }
                else if (a < 0)
                {
                    a = -a;
                    sub(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
                    sub(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
                }
            }
            else
            {
                int k;
                read(k);
                write(check(query(1, 0, N, k / DIGIT), k % DIGIT) ? 1 : 0), putchar('\n');
            }
        }
        return 0;
    }
}
int main()
{
#ifdef BlueSpirit
    freopen("3822.in", "r", stdin);
    freopen("3822.out", "w", stdout);
#endif
    return zyt::work();
}

【洛谷3822】[NOI2017] 整数(线段树压位).md

原文:https://www.cnblogs.com/zyt1253679098/p/10993308.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!