主席树是一种可持久化权值线段树,用于解决可查询历史版本的线段树的空间问题。
基于主席树的静态区间第K小问题要点如下:
- 建立 \(n\) 颗权值线段树,其中以 \(root[i]\) 为根的第 \(i\) 颗线段树储存 \([l, r]\) 区间内的权值信息
- 每一次新增一个结点,最多增加 \(log_2N\) 个结点
- 区间信息具有可加性,即 \([4,10]\) 区间内的结点个数等与 \([1,10]\) 减去 \([1,3]\)
- 查寻第 \(k\) 小,只需要根据区间内结点数多少二分第 \(k\) 小所在区间即可
说明 主席树空间计算:由要点2可以得出,至少需要 \(N * log_2(4 * N)\) 个结点
#include <cstdio> #include <algorithm> const int maxn = 2e5 + 10; int a[maxn], sortArray[maxn], b[maxn]; int root[maxn], t[maxn << 5][2], val[maxn << 5], cnt; #define lch(x) t[x][0] #define rch(x) t[x][1] template <class T> inline void read(T &x) { x = 0; int ch = getchar(); while (ch < '0' || ch > '9') ch = getchar(); while (ch >='0' && ch <='9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } } inline void build(int &o, int l, int r) { o = ++cnt; val[o] = 0; if (l == r) return; int mid = (l + r) >> 1; build(lch(o), l, mid); build(rch(o), mid + 1, r); } inline void update(int &o, int l, int r, int last, int p) { o = ++cnt; val[o] = val[last] + 1; int mid = (l + r) >> 1; lch(o) = lch(last), rch(o) = rch(last); if (l == r) return; if (p <= mid) update(lch(o), l, mid, lch(last), p); else update(rch(o), mid + 1, r, rch(last), p); } inline int query(int leftRange, int rightRange, int l, int r, int k) { if (l == r) return l; int mid = (l + r) >> 1, cnt = val[lch(rightRange)] - val[lch(leftRange)]; if (k > cnt) return query(rch(leftRange), rch(rightRange), mid + 1, r, k - cnt); else return query(lch(leftRange), lch(rightRange), l, mid, k); } /* inline int query(int o, int l, int r, int k) { if (l == r) return l; int mid = (l + r) >> 1; if (k > val[lch(o)]) return query(rch(o), mid + 1, r, k - val[lch(o)]); else return query(lch(o), l, mid, k); } */ int main() { int n, m; read(n), read(m); for (int i = 1; i <= n; ++ i) { read(a[i]), sortArray[i] = a[i]; } std::sort(sortArray + 1, sortArray + n + 1); int sortLen = std::unique(sortArray + 1, sortArray + n + 1) - sortArray - 1; for (int i = 1; i <= n; ++ i) b[i] = std::lower_bound(sortArray + 1, sortArray + sortLen + 1, a[i]) - sortArray; build(root[0], 1, sortLen); for (int i = 1; i <= n; ++ i) update(root[i], 1, sortLen, root[i - 1], b[i]); for (int i = 1, x, y, z; i <= m; ++ i) { read(x), read(y), read(z); printf("%d\n", sortArray[query(root[x - 1], root[y], 1, sortLen, z)]); } }
BZOJ3524/BZOJ2223
两道题更改输出即可
const int maxn = 5e5 + 10; #define lch(x) t[x][0] #define rch(x) t[x][1] #define mid ((l + r) >> 1) int a[maxn], root[maxn], tot; int t[maxn * 25][2], val[maxn * 25]; inline void build(int &o, int l, int r) { o = ++tot, val[o] = 0; if (l == r) return; build(lch(o), l, mid), build(rch(o), mid + 1, r); } inline void modify(int &o, int l, int r, int last, int p) { o = ++tot; val[o] = val[last] + 1; rch(o) = rch(last), lch(o) = lch(last); if (l == r) return; if (p <= mid) modify(lch(o), l, mid, lch(last), p); else modify(rch(o), mid + 1, r, rch(last), p); } inline int query(int Lrange, int Rrange, int l, int r, int cnt) { if (l == r) return l; if (val[rch(Rrange)] - val[rch(Lrange)] > cnt) return query(rch(Lrange), rch(Rrange), mid + 1, r, cnt); else if (val[lch(Rrange)] - val[lch(Lrange)] > cnt) return query(lch(Lrange), lch(Rrange), l, mid, cnt); return 0; } int main() { int n, m; read(n), read(m), build(root[0], 1, n); for (unsigned int i = 1; i <= n; ++ i) { read(a[i]), modify(root[i], 1, n, root[i - 1], a[i]); } int l, r; while (m --) { read(l), read(r); printf("%d\n", query(root[l - 1], root[r], 1, n, (r - l + 1) / 2)); } }