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

本文详细解析洛谷3822题目的解题思路,采用线段树优化算法,通过压缩位数减少复杂度,实现高效处理二进制位运算。文章深入讲解了加法和减法的位操作细节,以及如何利用线段树进行快速查找和修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

洛谷 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();
}

转载于:https://www.cnblogs.com/zyt1253679098/p/10993308.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值