[ABC339G] Smaller Sum 题解

本文介绍了一种使用分块技术对数组进行排序并计算区间和的高效算法,通过二分查找处理覆盖整块的区间查询,避免了大块长带来的性能问题,适用于大数据集的查询操作。
摘要由CSDN通过智能技术生成

原题链接

思路简述

比较明显的分块题,考虑用一个 b 数组存储 a 数组中每一个元素,分块之后再在 b 数组上对每一个块内元素进行排序。排完序后,对每一个块做一个前缀和,便于求和。对于每一次查询,对于覆盖了一整块的区间询问,可以用在 b 数组上二分查找的方式找出区间内小于等于 x 的元素之和;而对于没有覆盖一整个块的区间询问,直接暴力统计即可。

唯一要注意的事情:块长不能开为 n \sqrt n n ,会被卡掉;建议使用 2000 2000 2000 左右的块长。

代码

#include <bits/stdc++.h>
#define _FOR(i, a, b) for (int(i) = (a); (i) <= (b); ++(i))
typedef long long i64;

const int N = 2e5 + 5;

int n, pos[N], st[1005], ed[1005], Q;
i64 alpha, beta, garma, B, a[N], b[N], sum[N];

inline i64 Query(int l, int r, i64 val) {
    int p = pos[l], q = pos[r];
    i64 res = 0;
    if (p == q) {
        _FOR(i, l, r) {
            if (a[i] <= val) res += a[i];
        }
        return res;
    }
    _FOR(i, l, ed[p]) {
        if (a[i] <= val) res += a[i];
    }
    _FOR(i, st[q], r) {
        if (a[i] <= val) res += a[i];
    }
    _FOR(i, p + 1, q - 1) {
        if (b[st[i]] > val) continue;
        int L = st[i], R = ed[i];
        while (L < R) {
            int Mid = (L + R + 1) >> 1;
            if (b[Mid] <= val) L = Mid;
            else R = Mid - 1;
        }
        res += sum[L];
    }
    return res;
}

int main() {
    std::cin >> n;
    int block = 2000;
    int t = n / block + (n % block ? 1 : 0);
    _FOR(i, 1, n) {
        std::cin >> a[i];
        b[i] = a[i];
        pos[i] = (i - 1) / block + 1;
    }
    _FOR(i, 1, t) {
        st[i] = (i - 1) * block + 1;
        ed[i] = i * block;
    }
    ed[t] = n;
    _FOR(i, 1, t) {
        std::sort(b + st[i], b + ed[i] + 1);
        sum[st[i]] = b[st[i]];
        _FOR(j, st[i] + 1, ed[i]) {
            sum[j] = sum[j - 1] + b[j];
        }
    }
    std::cin >> Q;
    while (Q--) {
        std::cin >> alpha >> beta >> garma;
        i64 L = alpha ^ B, R = beta ^ B, X = garma ^ B;
        B = Query(L, R, X);
        std::cout << B << '\n';
    }
    return 0;
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值