【整体二分】【P3834】 【模板】可持久化线段树 1(主席树)
Description
给定一个长度为 \(n\) 的序列, \(m\) 次操作静态查询区间第 \(k\) 大
Input
第一行是 \(n,m\)
下一行描述这个序列
下面 \(m\) 行描述操作
Output
每个查询输出一行一个数代表答案
Hint
\(1~\leq~n,~m~\leq~2~\times~10^5\)
值域为 \([-1e9,~1e9]\)
Solution
考虑整体二分。
将操作和序列全部离线,混在一起操作,在每层中,如果一个插入操作插入的数大于 mid,则压入右边的vector,否则压入左边的vector,这样即可保证在每一层整个序列的插入操作只被操作 \(1\) 次。用树状数组维护不大于 mid 的插入点,插入点个数不小于 \(k\) 的查询压入左侧,否则 \(k~-=~\text{压入点个数}\) ,压入右侧即可。
注意一个区间内没有操作的时候要剪枝,否则复杂度会加上值域。
总复杂度 \(O((n + m)~\log^2 m)\)
Code
// luogu-judger-enable-o2
#include <cstdio>
#include <vector>
#include <iostream>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 200010;
const int INF = 1000000010;
struct OP {
int l, r, k, id;
};
std::vector<OP> Q;
int n, m;
int ans[maxn], tree[maxn];
int lowbit(int);
int query(int);
void update(int, const int);
void divide(int, int, std::vector<OP>&);
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(m);
for (int i = 1, x; i <= n; ++i) {
x = 0; qr(x); Q.push_back({-1, 0, x, i});
}
for (int i = 1, a, b, c; i <= m; ++i) {
a = b = c = 0; qr(a); qr(b); qr(c);
Q.push_back({a, b, c, i});
}
divide(-INF, INF, Q);
for (int i = 1; i <= m; ++i) qw(ans[i], '\n', true);
return 0;
}
void divide(int l, int r, std::vector<OP> &v) {
if (!v.size()) return;
if (l == r) {
for (auto i : v) if (i.l != -1) ans[i.id] = l;
return;
}
std::vector<OP>ldown, rdown;
int mid = (l + r) >> 1;
for (auto i : v) {
if (i.l == -1) {
if (i.k <= mid) {
update(i.id, 1);
ldown.push_back(i);
} else rdown.push_back(i);
}
}
for (auto i : v) {
if (i.l != -1) {
int k = query(i.r) - query(i.l - 1);
if ((k) >= i.k) ldown.push_back(i);
else {
i.k -= k; rdown.push_back(i);
}
}
}
for (auto i : ldown) {
if (i.l == -1) update(i.id, -1);
}
divide(l, mid, ldown);
divide(mid + 1, r, rdown);
}
inline int lowbit(int x) {return x & -x;}
void update(int x, const int v) {
while (x <= n) {
tree[x] += v;
x += lowbit(x);
}
}
int query(int x) {
int _ret = 0;
while (x) {
_ret += tree[x];
x -= lowbit(x);
}
return _ret;
}