「Luogu P3332」K大数查询

你需要维护 \(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;
}

你可能感兴趣的:(「Luogu P3332」K大数查询)