你需要维护 \(n\) 个可重整数集,集合的编号从 \(1\) 到 \(n\) 。
这些集合初始都是空集,有 \(m\) 个操作:
1 l r c
:表示将 \(c\) 加入到编号 \([l, r]\) 内的集合中2 l r c
:表示查询编号在 \([l, r]\) 内的集合的并集中,第 \(c\) 大的数是多少。
Luogu
分析
经典的整体二分题,这里是区间修改,所以用到的的树状数组也要是支持区间修改的,可以参考 树状数组
代码
#include
#define N 50003
#define rg register
#define lowbit(i) i&-i
#define ll long long
using namespace std;
int gi() {
int x = 0, f = 1; char c = getchar();
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
struct Opt {
int type, x, y, id; ll c;
} q[N], q1[N], q2[N];
int n, m, tot, turn;
ll s1[N], s2[N];
ll ans[N];
inline void add(int i, ll k) {
rg int x = i;
while (i <= n) {
s1[i] += k, s2[i] += x * k;
i += lowbit(i);
}
}
inline ll query(int i) {
rg ll ret = 0;
rg int x = i;
while (i) {
ret += s1[i] * (x + 1) - s2[i];
i -= lowbit(i);
}
return ret;
}
inline void solve(ll l, ll r, int L, int R) {
if (l > r || L > R) return;
if (l == r) {
for (rg int i = L; i <= R; ++i)
if (q[i].type == 2) ans[q[i].id] = l;
return;
}
rg ll mid = l + r >> 1;
rg int cnt1 = 0, cnt2 = 0;
for (rg int i = L; i <= R; ++i) {
if (q[i].type == 1) {
if (q[i].c > mid) {
add(q[i].x, 1), add(q[i].y + 1, -1);
q2[++cnt2] = q[i];
}
else q1[++cnt1] = q[i];
}
else {
rg ll tmp = query(q[i].y) - query(q[i].x - 1);
if (tmp >= q[i].c) q2[++cnt2] = q[i];
else q[i].c -= tmp, q1[++cnt1] = q[i];
}
}
for (rg int i = 1; i <= cnt2; ++i)
if (q2[i].type == 1) add(q2[i].x, -1), add(q2[i].y + 1, 1);
for (rg int i = 1; i <= cnt1; ++i) q[L + i - 1] = q1[i];
for (rg int i = 1; i <= cnt2; ++i) q[L + cnt1 + i - 1] = q2[i];
solve(l, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}
int main() {
n = gi(), m = gi();
for (rg int i = 1; i <= m; ++i) {
q[i] = (Opt){gi(), gi(), gi(), 0, 0};
scanf("%lld", &q[i].c);
if (q[i].type == 2) q[i].id = ++tot;
}
solve(0, n, 1, m);
for (rg int i = 1; i <= tot; ++i) printf("%lld\n", ans[i]);
return 0;
}