You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化std::vector数对中满足首值大于X且次值大于Y的数对计数查询?

高效解决方案:离线处理 + Fenwick树(树状数组)

嘿,你的暴力遍历解法在n和q都达到1e5的时候肯定会超时——毕竟O(q*n)的复杂度意味着1e10次操作,这远远超出了时间限制。下面给你一套高效的优化方案,核心思路是离线处理查询加上Fenwick树(树状数组),能把时间复杂度降到O((n+q)log(n+q)),完全能处理1e5级别的数据。

核心思路

我们把所有数对和查询都先排序,然后用树状数组动态维护符合条件的元素,批量处理查询,避免重复遍历。具体步骤如下:

1. 预处理:离散化+排序

  • 离散化Y值:因为数对的第二个元素和查询的Y可能范围极大(比如到1e9),直接用树状数组存不下。我们把所有出现过的Y(包括数对的第二个元素和所有查询的Y)收集起来,排序去重后给每个Y分配一个唯一的排名,把大数值范围压缩到我们能处理的大小(最多2e5个元素)。
  • 排序数对:把所有数对按第一个元素降序排列,如果第一个元素相同,第二个元素也降序排列。这样我们可以按顺序把首元素足够大的数对加入树状数组。
  • 排序查询:把所有查询按X降序排列,同时保存每个查询的原始索引,这样最后能把结果对应回原来的查询顺序。

2. 离线处理查询

  • 初始化一个空的Fenwick树,大小为离散化后的排名总数。
  • 用一个指针指向排序后的数对列表开头,遍历每个排序后的查询:
    • 把所有首元素>当前查询X的数对,将它们的第二个元素的离散化排名插入树状数组(也就是在对应位置加1)。
    • 现在树状数组里的所有元素都是首元素>X的数对的第二个元素。我们需要统计其中大于Y的数量:用树状数组里的总元素数,减去小于等于Y的元素数量(通过树状数组的前缀和查询得到),就是我们要的结果。
    • 把结果存到结果数组的对应原始索引位置。

3. 输出结果

最后按原始查询的顺序输出结果数组即可。

代码实现(C++)

#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>

using namespace std;

// Fenwick Tree 实现,用于高效的前缀和查询和单点更新
struct FenwickTree {
    vector<int> tree;
    int n;

    FenwickTree(int size) : n(size), tree(size + 1, 0) {}

    // 在idx位置增加delta(idx从1开始)
    void update(int idx, int delta) {
        while (idx <= n) {
            tree[idx] += delta;
            idx += idx & -idx;
        }
    }

    // 查询1到idx的前缀和
    int query(int idx) {
        int res = 0;
        while (idx > 0) {
            res += tree[idx];
            idx -= idx & -idx;
        }
        return res;
    }
};

int main() {
    // 加速输入输出,处理1e5规模数据必备
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<pair<int, int>> pairs(n);
    vector<int> all_ys;

    // 读取数对,收集所有Y值
    for (int i = 0; i < n; ++i) {
        cin >> pairs[i].first >> pairs[i].second;
        all_ys.push_back(pairs[i].second);
    }

    int q;
    cin >> q;
    // 存储查询:X, Y, 原始索引
    vector<tuple<int, int, int>> queries(q);
    for (int i = 0; i < q; ++i) {
        int X, Y;
        cin >> X >> Y;
        queries[i] = {X, Y, i};
        all_ys.push_back(Y);
    }

    // 离散化Y值:排序+去重
    sort(all_ys.begin(), all_ys.end());
    all_ys.erase(unique(all_ys.begin(), all_ys.end()), all_ys.end());
    // 获取Y对应的排名(从1开始)
    auto get_rank = [&](int y) {
        return lower_bound(all_ys.begin(), all_ys.end(), y) - all_ys.begin() + 1;
    };

    // 按首元素降序排序数对,首元素相同则按次元素降序
    sort(pairs.begin(), pairs.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
        if (a.first != b.first) return a.first > b.first;
        return a.second > b.second;
    });

    // 按X降序排序查询,这样可以批量处理符合条件的数对
    sort(queries.begin(), queries.end(), [](const tuple<int, int, int>& a, const tuple<int, int, int>& b) {
        return get<0>(a) > get<0>(b);
    });

    FenwickTree ft(all_ys.size());
    vector<int> ans(q);
    int pair_ptr = 0; // 指向当前要处理的数对

    for (const auto& qry : queries) {
        int X = get<0>(qry);
        int Y = get<1>(qry);
        int original_idx = get<2>(qry);

        // 把所有首元素>X的数对加入树状数组
        while (pair_ptr < n && pairs[pair_ptr].first > X) {
            int rank = get_rank(pairs[pair_ptr].second);
            ft.update(rank, 1);
            pair_ptr++;
        }

        // 计算大于Y的数量:总元素数 - 小于等于Y的元素数
        int total_valid = pair_ptr;
        int y_rank = get_rank(Y);
        int cnt_less_or_eq = ft.query(y_rank);
        ans[original_idx] = total_valid - cnt_less_or_eq;
    }

    // 按原始顺序输出结果
    for (int result : ans) {
        cout << result << '\n';
    }

    return 0;
}

关键注意事项

  • 离散化的完整性:一定要把所有数对的第二个元素和查询的Y都包含进去,否则会出现找不到排名的情况。
  • 树状数组的索引:树状数组的实现通常从1开始,所以离散化后的排名也要从1开始,避免0索引导致的错误。
  • 输入输出加速:对于1e5级别的数据,必须用ios::sync_with_stdio(false);和cin.tie(nullptr);来关闭同步,否则输入输出会很慢。
  • 负数处理:如果Y可能是负数,离散化的方法依然适用,因为排序和去重不区分正负。

内容的提问来源于stack exchange,提问作者Meenu Meena

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 20:32:31