「ZJOI 2013」K 大数查询

题目链接:BZOJ 3110

有 $n$ 个位置,$m$ 个操作。操作分为以下 $2$ 种:

  • 1 a b c:表示在第 $a$ 个位置到第 $b$ 个位置,每个位置加入一个数 $c$。
  • 2 a b c:表示询问从第 $a$ 个位置到第 $b$ 个位置,第 $c$ 大的数是多少。

数据范围:$1\le n,m\le 5\times 10^4$,操作 $1$ 中 $0\le \vert c \vert \le 2 ^ {63} - 1$。


Solution

我们考虑整体二分。用树状数组维护区间和——树状数组是可以支持区间修改、区间查询的,并且复杂度还是 $\mathcal O(\log n)$ 的(众所周知应该是 $\mathcal O(1)$ 的)。然后按照套个板子就做完了。

时间复杂度:$\mathcal O(n\log^2 n)$。


Code

#include <cstdio>
#include <algorithm>

typedef long long LL;

const int N = 2e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3f;

int n, m;
LL ans[N];

struct Data {
    int opt, x, y, idx;
    LL k;
} q[N], q1[N], q2[N];

struct BIT {
    LL c1[N], c2[N];
    void modify(int x, LL v) {
        int _x = x;
        for (; x <= n; x += x & -x) {
            c1[x] += v, c2[x] += 1LL * _x * v;
        }
    }
    LL query(int x) {
        int _x = x;
        LL ans = 0;
        for (; x; x ^= x & -x) {
            ans += 1LL * (_x + 1) * c1[x] - c2[x];
        }
        return ans;
    }
} bit;

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].opt == 2) ans[q[i].idx] = l;
        }
        return;
    }
    LL mid = (l + r) >> 1;
    int t1 = 0, t2 = 0;
    for (int i = L; i <= R; i++) {
        if (q[i].opt == 1) {
            if (q[i].k > mid) {
                q2[++t2] = q[i];
                bit.modify(q[i].x, 1);
                bit.modify(q[i].y + 1, -1);
            } else {
                q1[++t1] = q[i];
            }
        } else {
            LL sum = bit.query(q[i].y) - bit.query(q[i].x - 1);
            if (sum >= q[i].k) {
                q2[++t2] = q[i];
            } else {
                q[i].k -= sum;
                q1[++t1] = q[i];
            }
        }
    }
    for (int i = 1; i <= t2; i++) {
        if (q2[i].opt == 1) {
            bit.modify(q2[i].x, -1);
            bit.modify(q2[i].y + 1, 1);
        }
    }
    std::copy(q1 + 1, q1 + t1 + 1, q + L);
    std::copy(q2 + 1, q2 + t2 + 1, q + L + t1);
    solve(l, mid, L, L + t1 - 1);
    solve(mid + 1, r, L + t1, R);
}
int main() {
    scanf("%d%d", &n, &m);
    int cnt = 0;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d%lld", &q[i].opt, &q[i].x, &q[i].y, &q[i].k);
        if (q[i].opt == 2) q[i].idx = ++cnt;
    }
    solve(-INF, INF, 1, m);
    for (int i = 1; i <= cnt; i++) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}

发表评论