• 【树套树】 带修主席树


    Algorithm

    Task

    给定一个长度为 (n) 的序列,要求支持单点修改和区间 (kth) 查询,强制在线。

    Limitation

    如果认为输入数据全部与 (n) 同阶,要求算法时空复杂度 (O(n log^2n))

    Solution

    其实这个东西和可持久化线段树没有半毛钱关系,实质上就是树状数组套权值线段树

    考虑如果不带修,那么可以直接主席树解决。主席树的本质是在每个位置维护一个从 (1) 到该位置的前缀权值线段树。

    如果暴力上主席树,单次修改是 (O(n log n)) 的,导致GG。

    考虑将前缀的权值线段树分成多段,这几段权值线段树相加即是总的前缀权值线段树。如果分成了 (O(T)) 段,那么修改复杂度将是 (O(T log n))。注意这里分成多段以后每一段是区间内的普通权值线段树,而不是主席树。

    考虑用树状数组维护这个前缀,那么每个前缀区间都能够被分成 (O(log n)) 段,每一段是对应区间的权值线段树,这样单次修改的复杂度是 (O(log^2n))

    考虑查询前缀,只需要找出这 (O(log n)) 个前缀,然后一起二分即可。时间复杂度 (O(log^2n))

    于是总时间复杂度 (O(n log^2n))使用动态开点可以做到空间复杂度 (O(n log^2n))

    Sample

    Description

    给定一个长度为 (n) 的序列,有 (m) 次操作,要么单点修改,要么查询区间 (kth)

    Limitation

    (1 leq n,~m leq 10^5)

    序列值域在 (10^9) 范围内

    Solution

    板板题要什么Solution

    Code

    我的代码常数好大啊……是 @DDOSvoid 大爷常数的两倍…… 感觉常数主要大在了查询的时候存 (O(log n)) 个节点的 std::vector 上了,大概换成手写会快好多叭……

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    const int maxn = 100005;
    
    int n, m, upceil;
    int MU[maxn];
    std::vector<int>tmp;
    
    struct Tree {
      Tree *ls, *rs;
      int v;
    
      Tree() : ls(NULL), rs(NULL), v(0) {}
    };
    Tree *rot[maxn];
    
    struct Ask {
      char ch;
      int a, b, c;
    };
    Ask ask[maxn];
    
    void init_hash();
    void update(int p, const int v);
    int Query(int l, int r, int k);
    void Update(const int p, const int v);
    void query(std::vector<int> &a, int &v);
    void update(Tree *const u, const int l, const int r, const int p, const int v);
    
    int main() {
      freopen("1.in", "r", stdin);
      qr(n); qr(m);
      rot[0] = new Tree;
      for (int i = 1; i <= n; ++i) {
        rot[i] = new Tree;
        qr(MU[i]);
      }
      char ch; int a, b, c;
      for (int M = 1; M <= m; ++M) {
        do ch = IPT::GetChar(); while ((ch != 'Q') && (ch != 'C'));
        if (ch == 'Q') {
          a = b = c = 0; qr(a); qr(b); qr(c);
        } else {
          a = b = 0; qr(a); qr(b);
        }
        ask[M] = {ch, a, b, c};
      }
      init_hash();
      for (int i = 1; i <= n; ++i) {
        update(i, 1);
      }
      for (int i = 1; i <= m; ++i) {
        ch = ask[i].ch; a = ask[i].a; b = ask[i].b; c = ask[i].c;
        if (ch == 'Q') {
          qw(Query(a, b, c), '
    ', true);
        } else {
          Update(a, b);
        }
      }
      return 0;
    }
    
    void init_hash() {
      for (int i = 1; i <= n; ++i) tmp.push_back(MU[i]);
      for (int i = 1; i <= m; ++i) if (ask[i].ch == 'C') {
        tmp.push_back(ask[i].b);
      }
      std::sort(tmp.begin(), tmp.end());
      upceil = std::unique(tmp.begin(), tmp.end()) - tmp.begin();
      auto ed = tmp.begin() + upceil;
      for (int i = 1; i <= n; ++i) {
        MU[i] = std::lower_bound(tmp.begin(), ed, MU[i]) - tmp.begin() + 1;
      }
      for (int i = 1; i <= m; ++i) if (ask[i].ch == 'C') {
        ask[i].b = std::lower_bound(tmp.begin(), ed, ask[i].b) - tmp.begin() + 1;
      }
    }
    
    inline int lowbit(const int x) { return x & -x; }
    
    void update(int p, const int v) {
      int pv = MU[p];
      do update(rot[p], 1, upceil, pv, v); while ((p += lowbit(p)) <= n);
    }
    
    void update(Tree *const u, const int l, const int r, const int p, const int v) {
      u->v += v;
      if (l == r) return;
      int mid = (l + r) >> 1;
      if (p <= mid) {
        update(u->ls ? u->ls : u->ls = new Tree, l, mid, p, v);
      } else {
        update(u->rs ? u->rs : u->rs = new Tree, mid + 1, r, p, v);
      }
    }
    
    void Update(const int p, const int v) {
      update(p, -1);
      MU[p] = v;
      update(p, 1);
    }
    
    void query(std::vector<Tree*> &a, int &v) {
      if (!a.size()) return;
      for (auto u : a) if (u->ls) {
        v += u->ls->v;
      }
    }
    
    void cls(std::vector<Tree*> &a, std::vector<Tree*> &b) {
      for (auto u : a) if (u->ls) {
        b.push_back(u->ls);
      }
    }
    
    void crs(std::vector<Tree*> &a, std::vector<Tree*> &b) {
      for (auto u : a) if (u->rs) {
        b.push_back(u->rs);
      }
    }
    
    int Query(int l, int r, int k) {
      int tl = 1, tr = upceil; --l;
      std::vector<Tree*> vl[2], vr[2];
      int key = 0, tk = 1;
      do vr[0].push_back(rot[r]); while (r -= lowbit(r));
      do vl[0].push_back(rot[l]); while (l -= lowbit(l));
      while (tl != tr) {
        int mid = (tl + tr) >> 1;
        int s1 = 0, s2 = 0;
        query(vr[key], s1); query(vl[key], s2);
        int sum = s1 - s2;
        if (sum >= k) {
          cls(vr[key], vr[tk]);
          cls(vl[key], vl[tk]);
          tr = mid;
        } else {
          crs(vr[key], vr[tk]);
          crs(vl[key], vl[tk]);
          tl = mid + 1;
          k -= sum;
        }
        vr[key].clear(); vl[key].clear();
        std::swap(key, tk);
      }
      return tmp[tl - 1];
    }
    
  • 相关阅读:
    vs2005 水晶报表横向打印Bug
    petshop4.0 详解之七(PetShop表示层设计)
    petshop4.0 详解之八(PetShop表示层设计)
    在VS2005中使用VSS2005
    用DataFormatString格式化GridView
    GridView的高级用法
    水晶报表 打印时出现错误提示:出现通信错误。将停止打印
    POJ1182 食物链[并查集]
    并查集的基础知识
    HDOJ1269 迷宫城堡[强连通分量]
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11124029.html
Copyright © 2020-2023  润新知