• LOJ107. 维护全序集【树状数组维护全序集】


    题目描述

    这是一道模板题,其数据比「普通平衡树」更强。

    如未特别说明,以下所有数据均为整数。

    维护一个多重集 S ,初始为空,有以下几种操作:

    把 x 加入 S
    删除 S 中的一个 x,保证删除的 x 一定存在
    求 S 中第 k 小
    求 S 中有多少个元素小于 x
    求 S 中小于 x 的最大数
    求 S 中大于 x 的最小数
    操作共 n 次。

    输入格式

    第一行一个整数 n,表示共有 n 次操作 。

    接下来 n 行,每行为以下几种格式之一 :

    0 x,把 x 加入 S
    1 x,删除 S 中的一个 x,保证删除的数在 S 中一定存在
    2 k,求 S 中第 k 小的数,保证要求的数在 S 中一定存在
    3 x,求 S 中有多少个数小于 x
    4 x,求 S 中小于 x 的最大数,如果不存在,输出 −1
    5 x,求 S 中大于 x 的最小数,如果不存在,输出 −1

    输出格式

    对于每次询问,输出单独一行表示答案。

    样例

    样例输入

    5
    0 3
    0 4
    2 2
    1 4
    3 3

    样例输出

    4
    0

    数据范围与提示

    $ 1 leq n leq 3 imes 10 ^ 5, 0 leq x leq 10 ^ 9$


    思路

    平衡数已经烂大街了
    我就来讲讲bit怎么维护全序集吧

    除了bit的常规操作,核心技能只有一个
    就是找序列的第k大
    这个东西是可以在树状数组上二分的
    你会发现对于任何一个bit上的节点
    它左子树上的任何一个节点都是对应右子树上的一个节点加上一个二进制位得到的
    并且这个二进制位恰好是这个节点儿子的层数所对应的二进制位
    所以就可以直接看这个二进制位到底要不要选
    在bit上二分并迭代就可以了

    好神仙的操作啊


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define fu(a, b, c) for (int a = b; a <= c; ++a)
    #define fd(a, b, c) for (int a = b; a >= c; --a)
    #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x) {
      bool w = 1;x = 0;
      char c = getchar();
      while (!isdigit(c) && c != '-') c = getchar();
      if (c == '-') w = 0, c = getchar();
      while (isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if (!w) x = -x;
    }
    template <typename T>
    void Write(T x) {
      if (x < 0) {
        putchar('-');
        x = -x; 
      }
      if (x > 9) Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    //树状数组维护全序集
    const int N = 1e6 + 10;
    int tot = 0, n, up = 1;
    int a[N], b[N], op[N];
    int bit[N], lowbit[N];
    inline void insert(int vl) {
      for (; vl <= tot; vl += lowbit[vl]) ++bit[vl];
    }
    inline void del(int vl) {
      for (; vl <= tot; vl += lowbit[vl]) --bit[vl];
    }
    inline int query(int vl) {
      int res = 0;
      for (; vl; vl -= lowbit[vl]) res += bit[vl];
      return res;
    }
    inline int kth(int vl) {
      int res = 0;
      for (int i = up; i; i >>= 1) {
        if (bit[res | i] < vl && (res | i) <= tot) {
          res |= i;
          vl -= bit[res];
        }
      }
      return res + 1;
    }
    inline int getpre(int vl) {return kth(query(vl - 1));}
    inline int getnxt(int vl) {return kth(query(vl) + 1);}
    int main() {
      Read(n);
      fu(i, 1, n) {
        Read(op[i]);
        Read(a[i]);
        if (op[i] != 2) b[++tot] = a[i];
      }
      b[++tot] = -1;
      sort(b + 1, b + tot + 1);
      tot = unique(b + 1, b + tot + 1) - b - 1;
      b[tot + 1] = -1;
      fu(i, 1, tot) lowbit[i] = i & (-i);
      while (up <= tot) up <<= 1; up >>= 1;
      fu(i, 1, n) {
        if (op[i] != 2) a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
        switch (op[i]) {
          case 0:insert(a[i]);break;
          case 1:del(a[i]);break;
          case 2:Write(b[kth(a[i])]);putchar('
    ');break;
          case 3:Write(query(a[i] - 1));putchar('
    ');break;
          case 4:Write(b[getpre(a[i])]);putchar('
    ');break;
          case 5:Write(b[getnxt(a[i])]);putchar('
    ');break;
        }
      }
      return 0;
    }
    
  • 相关阅读:
    【和我一起学python吧】Python安装、配置图文详解
    【和我一起学python吧】初学Python,版本如何选择?
    CSS使用简介
    css样式表中的样式覆盖顺序
    转载-ActiveMQ通过JAAS实现的安全机制
    消息队列开发记录笔记-ActiveMQ
    转载-使用消息队列的 10 个理由
    在linux或mac终端下将命令结果输出到文件保存
    ideviceinstaller命令(类似android的adb)
    mac安装mysql及导库
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9788132.html
Copyright © 2020-2023  润新知