Skip to content

P10814 【模板】离线二维数点

原题链接:P10814 【模板】离线二维数点

Tag:二维数点、

题目描述

给你一个长为 \(n\) 的序列 \(a\),有 \(m\) 次询问,每次询问给定 \(l,r,x\),求 \([l,r]\) 区间中小于等于 \(x\) 的元素个数。

数据说明:

\(1\le n,m,a_i,l,r,x\le 2\times10^6\)

分析

我们可以把题目中的 \(a_i\) 理解成二维平面上的点 \((i,a_i)\)。如下图所示:

p1.png

之后反观题里我们要处理的查询, 在二维平面上的几何意义。

p1.png

我们会发现当我们把数字放到二维平面上之后, 查询的本质就变成了在一个矩形中有多少点。 仔细观察发现我们要求的这个区间 \([l,r]\) 里有多少点, 实际上可以通过用区间 \([1,r]\) 的点减去区间 \([1,l-1]\) 的点来实现。

这启示我们可以把一个查询拆成如上的两部分,记录这个查询的 \(id\), 以及这一部分是要加上的还是要减去的, 把全部查询离线下来进行求解。

具体求解过程是我们从左向右去做,维护之前有多少个比 \(x\) 小的数字, 这是一个类逆序对问题,我们可以使用 \(\text{Fenwick}\) 求解。

代码实现

struct Fenwick {
    static const int fenwickMax = 2e6 + 5;
    int n;
    int tree[fenwickMax];
    int lowBit(int x) { return -x & x; }
    void init(int x) { n = x; }
    void add(int x, int k) {
        while (x <= n) {
            tree[x] += k;
            x += lowBit(x);
        }
    }
    int query(int x) {
        int ans = 0;
        while (x) {
            ans += tree[x];
            x -= lowBit(x);
        }
        return ans;
    }
};
Fenwick fenwick;
void NeverSayNever() {
    struct query {
        int x, id, tag;
    };
    int n, m;
    cin >> n >> m;
    fenwick.init(n);
    vector<int> vec(n + 1);
    for (int i = 1; i <= n; ++i) {
        cin >> vec[i];
    }
    vector<vector<query>> q(n + 1);
    vector<int> ans(m + 1);
    for (int i = 1; i <= m ; ++i) {
        int l,r,x; cin >> l >> r >> x;
        q[l-1].push_back({x, i, -1});
        q[r].push_back({x,i,1});
    }
    for (int i = 1; i <= n ; ++i) {
        fenwick.add(vec[i],1);
        for(auto &[x,id,tag] : q[i]){
            ans[id] += tag * fenwick.query(x);
        }
    }
    for (int i = 1; i <= m ; ++i) {
        cout << ans[i] << endl;
    }
}

额外思考

笑点解析: 写这个题解的当天是 \(\text{ICPC}\) 网络赛第一场当天, 但是我因为学校的逆天安排不能打, 报名费打水漂了, 而且法定节假日我还在上早八。

日志

本页面创建于 2024/09/15 09:22