你需要维护 \(n\) 个可重整数集,集合的编号从 \(1\) 到 \(n\) 。
这些集合初始都是空集,有 \(m\) 个操作:
1 l r c
:表示将 \(c\) 加入到编号 \([l, r]\) 内的集合中2 l r c
:表示查询编号在 \([l, r]\) 内的集合的并集中,第 \(c\) 大的数是多少。
分析
经典的整体二分题,这里是区间修改,所以用到的的树状数组也要是支持区间修改的,可以参考 树状数组
代码
#include <bits/stdc++.h> #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; }
来源:https://www.cnblogs.com/hlw1/p/12270143.html