天天看點

20200903 專題:值域分塊

總覽:

做了一道毒瘤題 帶插區間k小

并且樹套樹常數過大,過不去 (毒瘤出題人毒瘤卡常)

于是就學習了新科技:值域分塊

實質是分塊套分塊,先給序列分塊,再在每個塊中給值域分塊

(分塊用連結清單實作好友善

具體題目具體分析

T1 P4278 帶插入區間K小值

思路:

bzoj一個點平均15s,luogu一個點1s……

先對序列分塊,友善取出 [ l , r ] [l,r] [l,r]

再對值域分塊,友善查詢 k k k 小

要 O ( 1 ) O(1) O(1) 查詢 [ l , r ] [l,r] [l,r] 中值在 [ v a l l , v a l r ] [val_l,val_r] [vall​,valr​] 中的數的個數

于是可以對值域做序列上的字首和

查詢時先 O ( n ) O(\sqrt n) O(n

​) 将塊取出,再 O ( n ) O(\sqrt n) O(n

​) 找到 k k k 小

修改,插入時要更新每個序列塊中一個值域塊字首和, O ( n ) O(\sqrt n) O(n

​)

如果在一個塊中插入過多,将這個塊分裂成兩個塊即可

塊狀連結清單實作

找個人少的時候卡過去

代碼:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char buf_[1 << 21];
int p_ = -1;
inline void flush() {
  fwrite(buf_, 1, p_ + 1, stdout);
  p_ = -1;
}
inline void pc(char x) {
  if (p_ == (1 << 21) - 1) flush();
  buf_[++p_] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) {
    pc('0');
    return;
  }
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++pos] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = pos; i; i--) pc(k[i]);
  return;
}
}  // namespace IO
using namespace IO;

const int A = 7e4;
const int SA = 275;
int n, blo, Q;
struct block {
  int ls, rs, sz;
  int s[(SA << 1) + 50], v[SA + 10], w[A + 10];
} t[SA + 10];
int rt, tot;
int ans;

#define ls(x) t[x].ls
#define rs(x) t[x].rs
inline void prepare() {
  n = in();
  rt = 0, tot = n / SA + 1;
  for (int i = 1; i <= n; i++) {
    int u = in(), now = i / SA + 1;
    t[now].s[++t[now].sz] = u;
    t[now].w[u]++, t[now].v[u / SA + 1]++;
  }
  for (int i = 0; i <= tot; i++) {
    if (i) {
      ls(i) = i - 1;
      for (int j = 0; j <= A; j++) t[i].w[j] += t[i - 1].w[j];
      for (int j = 1; j <= SA; j++) t[i].v[j] += t[i - 1].v[j];
    }
    if (i != tot) rs(i) = i + 1;
  }
  return;
}

inline void split(int now) {
  t[++tot] = t[now];
  ls(tot) = ls(now), rs(tot) = now;
  rs(ls(now)) = tot, ls(now) = tot;
  for (int i = SA + 1; i <= t[now].sz; i++) {
    int k = t[now].s[i];
    t[now].s[i - SA] = k;
    t[tot].w[k]--;
    t[tot].v[k / SA + 1]--;
  }
  t[tot].sz = SA, t[now].sz -= SA;
  return;
}

inline void insert(int x, int k) {
  int now = rs(rt);
  while (x - 1 > t[now].sz) x -= t[now].sz, now = rs(now);
  t[now].sz++;
  for (int i = t[now].sz; i > x; i--) t[now].s[i] = t[now].s[i - 1];
  t[now].s[x] = k;
  int tmp = now;
  while (now) {
    t[now].w[k]++, t[now].v[k / SA + 1]++;
    now = rs(now);
  }
  if (t[tmp].sz >= (SA << 1)) split(tmp);
}

inline void change(int x, int k) {
  int now = rs(rt);
  while (x > t[now].sz) x -= t[now].sz, now = rs(now);
  int u = t[now].s[x];
  t[now].s[x] = k;
  while (now) {
    t[now].w[u]--, t[now].w[k]++;
    t[now].v[u / SA + 1]--, t[now].v[k / SA + 1]++;
    now = rs(now);
  }
  return;
}

int V[SA], W[A];
inline int qurey(int x, int y, int k) {
  int l = rs(rt), r = rs(rt);
  while (x > t[l].sz) x -= t[l].sz, l = rs(l);
  while (y > t[r].sz) y -= t[r].sz, r = rs(r);
  for (int i = 1; i < x; i++) {
    int u = t[l].s[i];
    W[u]--, V[u / SA + 1]--;
  }
  for (int i = 1; i <= y; i++) {
    int u = t[r].s[i];
    W[u]++, V[u / SA + 1]++;
  }
  int now = 1;
  while (k > V[now] + (t[ls(r)].v[now] - t[ls(l)].v[now]))
    k -= V[now] + (t[ls(r)].v[now] - t[ls(l)].v[now]), now++;
  int res = SA * (now - 1);
  while (k > W[res] + (t[ls(r)].w[res] - t[ls(l)].w[res]))
    k -= W[res] + (t[ls(r)].w[res] - t[ls(l)].w[res]), res++;
  for (int i = 1; i < x; i++) {
    int u = t[l].s[i];
    W[u]++, V[u / SA + 1]++;
  }
  for (int i = 1; i <= y; i++) {
    int u = t[r].s[i];
    W[u]--, V[u / SA + 1]--;
  }
  return res;
}
#undef ls
#undef rs

signed main() {
  prepare();
  Q = in();
  while (Q--) {
    char opt = ch();
    while (opt != 'Q' && opt != 'M' && opt != 'I') opt = ch();
    if (opt == 'Q') {
      int u = in() ^ ans, v = in() ^ ans, k = in() ^ ans;
      out(ans = qurey(u, v, k)), pc('\n');
    } else if (opt == 'M') {
      int u = in() ^ ans, k = in() ^ ans;
      change(u, k);
    } else {
      int u = in() ^ ans, k = in() ^ ans;
      insert(u, k);
    }
  }
  flush();
  return 0;
}
           

繼續閱讀