• luoguP4587[FJOI2016]神秘数字(主席树)


    原题面

    洛谷P4587

    题目描述

    定义一个可重复数字集合(S)的神秘数为最小的不能被(S)的子集的元素和表示的数
    例如:(S = {1,1,1,4,13})
    则:(1=1 \ 2 = 1 + 1 \ 3 = 1 + 1 + 1 \ 4 = 4 \ 5 = 1 + 4 \ 6 = 1 + 1 + 4 \ 7 = 1 + 1 + 1 + 4)
    8不能被(S)的子集和表示,所以(S)的神秘数是8
    现有一个长为(n)的序列,(m)次询问,每次询问输入(l,r),求(S={a_i | l le i le r })的神秘数

    解析

    先考虑一个询问怎么处理
    (l)(r)的数排序后考虑依次加入每一个数
    假设当前可以凑出([1, t))的数,那么计算(sum = sum_{a_i le t} {a_i})
    如果(sum < t),显然(t)就是答案
    如果(sum ge t),说明凑出([1, t))后一定剩下一个小于等于(t)的数,那么([1, sum])的数也可以凑出来,(t)移动到(sum + 1)
    然后考虑多组询问,我们要快速查询([l, r])内小于等于(t)(a_i)之和,就可以套主席树了

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MAXN 100005
    #define MAXA 1000000000
    
    typedef long long LL;
    struct ChairmanTree {
        struct Node {
            Node *ls, *rs;
            int sum;
        } * root[MAXN];
        void build();
        void build(Node *, Node *, int, int, int);
        int query(Node *, Node *, int, int, int, int);
    } T;
    int N, M, ans;
    int a[MAXN];
    
    char gc();
    LL read();
    void print(LL);
    int main() {
        N = read();
        for (int i = 1; i <= N; ++i) a[i] = read();
        T.build();
        M = read();
        while (M--) {
            int l = read() - 1, r = read();
            ans = 1;
            while (1) {
                int s = T.query(T.root[l], T.root[r], 1, MAXA, 1, ans);
                if (s >= ans)
                    ans = s + 1;
                else
                    break;
            }
            print(ans);
            putchar('
    ');
        }
        return 0;
    }
    void ChairmanTree::build() {
        for (int i = 1; i <= N; ++i) build(root[i - 1], root[i] = new Node(), 1, MAXA, a[i]);
    }
    void ChairmanTree::build(Node *pre, Node *cur, int L, int R, int v) {
        if (L == R)
            cur->sum = (pre ? pre->sum : 0) + v;
        else {
            int mid = (L + R) >> 1;
            if (v <= mid) {
                if (pre) cur->rs = pre->rs;
                build(pre ? pre->ls : NULL, cur->ls = new Node(), L, mid, v);
            } else {
                if (pre) cur->ls = pre->ls;
                build(pre ? pre->rs : NULL, cur->rs = new Node(), mid + 1, R, v);
            }
            cur->sum = 0;
            if (cur->ls) cur->sum += cur->ls->sum;
            if (cur->rs) cur->sum += cur->rs->sum;
        }
    }
    int ChairmanTree::query(Node *pre, Node *cur, int L, int R, int l, int r) {
        if (!cur) return 0;
        if (L >= l && R <= r) return cur->sum - (pre ? pre->sum : 0);
        int mid = (L + R) >> 1, res = 0;
        if (l <= mid) res += query(pre ? pre->ls : NULL, cur->ls, L, mid, l, r);
        if (r > mid) res += query(pre ? pre->rs : NULL, cur->rs, mid + 1, R, l, r);
        return res;
    }
    inline char gc() {
        static char buf[1000000], *p1, *p2;
        if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin);
        return p1 == p2 ? EOF : *p2++;
    }
    inline LL read() {
        LL res = 0;
        char ch = gc();
        while (ch < '0' || ch > '9') ch = gc();
        while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc();
        return res;
    }
    inline void print(LL x) {
        static int buf[30];
        if (!x)
            putchar('0');
        else {
            while (x) buf[++buf[0]] = x % 10, x /= 10;
            while (buf[0]) putchar('0' + buf[buf[0]--]);
        }
    }
    //Rhein_E
    
  • 相关阅读:
    Python SOCKET网络编程
    网络基础 -- 子网划分
    网络基础 -- 网络协议篇
    面向对象练习:学生选课系统
    python 异常处理 断言
    Python 面向对象 中高级
    Python 面向对象 基础
    Python 练习
    Python 练习计算器
    Python 正则表达式
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10394493.html
Copyright © 2020-2023  润新知