【树套树】 带修主席树

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), '\n', 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];
}
posted @ 2019-07-03 00:13  一扶苏一  阅读(992)  评论(0编辑  收藏  举报