【学习笔记】整体二分
思路:
设 \([l,r],[L,R]\) 分别为答案的值域和定义域。整体二分的过程中像主席树一样按时间顺序存入数组分治:
-
先找到当前答案与 \(mid\) 之间的关系,并分类。
-
分好类,以其作为下一层分治的数组。
例题:
区间第k小:
题目:Luogu P3834 【模板】可持久化线段树 2(主席树)
离线整体二分一次即可。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ZYC using
#define AK namespace
#define IOI std
#define ll long long
ZYC AK IOI;
const int N = 1e5 + 10, M = 5e4 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m;
ll t[N], ans[N];
void modify(int x, ll val) {for (int i = x; i <= n; i += i & -i) t[i] += val;}
ll query(int x) {ll ans = 0;for (int i = x; i; i -= i & -i) ans += t[i]; return ans;}
struct node
{
int x, y, k, id;
}q[N + M], q1[N + M], q2[N + M];
void Solve (ll l, ll r, int L, int R)
{
if (l > r || L > R) return ;
if(l == r)
{
for (int i = L; i <= R; i++) if(q[i].id) ans[q[i].id] = l;
return ;
}
ll mid = l + r >> 1;
int n1 = 0, n2 = 0;
for (int i = L; i <= R; i ++)
if (q[i].id)
{
ll tmp = query(q[i].y) - query(q[i].x - 1);
if (q[i].k <= tmp) q1[++n1] = q[i];
else q[i].k -= tmp, q2[++n2] = q[i];
}
else
if (q[i].x <= mid) modify(q[i].y, 1), q1[++n1] = q[i];
else q2[++n2] = q[i];
for (int i = 1; i <= n1; i++) if(!q1[i].id) modify(q1[i].y, -1);
for (int i = 1; i <= n1; i++) q[L + i - 1] = q1[i];
for (int i = 1; i <= n2; i++) q[L + n1 + i - 1] = q2[i];
Solve(l, mid, L, L + n1 - 1), Solve(mid + 1, r, L + n1, R);
}
int main()
{
n = Read(), m = Read();
for (int i = 1; i <= n; i++) q[i].x = Read(), q[i].y = i;
for (int i = 1; i <= m; i++)
q[i + n].x = Read(), q[i + n].y = Read(), q[i + n].k = Read(), q[i + n].id = i;
Solve(-1e9, 1e9, 1, n + m);
for (int i = 1; i <= m; i++) printf ("%lld\n", ans[i]);
return 0;
}
To be continued...