P10814 【模板】离线二维数点 题解

题目传送门

思路

一眼主席树板子题,但是一看数据范围 \(n,m\le 2\times 10 ^ 6\)似了

在线做法应该是似完了,考虑离线做法。

我们知道树状数组是可以做二维偏序的,大家应该都知道一个经典问题:对于一个序列,多次询问下标 \(\le a\) 且数值 \(\le b\) 的数的个数。

回到这道题,相比上面只多了一个左端点的限制当然还有数据范围,所以可以采用类似前缀和的方法,对于一个询问 \(\{l, r, x\}\),用 \([1, r]\) 的这种数的个数减去 \([1, l - 1]\) 中的即可。

那么可以将询问拆成两部分,一部分是左端点减一,一部分是右端点,同时还要记录符号,左端点是减,右端点是加。
接下来就可以像扫描线一样,将 \(pos\) 按从小到大排序,然后从左到右扫描一遍计算答案就行了。

时间复杂度 \((n\log {2\cdot 10 ^ 6} + m\log m + m\log n)\),若都与 \(n\) 同级就为 \(O(n\log n)\)

\(\texttt{Code:}\)

#include <iostream>
#include <algorithm>

#define lowbit(x) x & -x
using namespace std;

const int N = 2000010;

int n, m;
int a[N];
struct BIT{
    int c[N];
    void add(int x, int y) {
        for(; x < N; x += lowbit(x)) c[x] += y;
    }
    int ask(int x) {
        int res = 0;
        for(; x; x -= lowbit(x)) res += c[x];
        return res; 
    }
}tr;

struct node{
    int id, pos, x, sign;
    bool operator< (const node &o) const {
        return pos < o.pos;
    }
}q[N << 1];
int tt;
int ans[N];

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    int l, r, x;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &l, &r, &x);
        q[++tt] = {i, l - 1, x, -1};
        q[++tt] = {i, r, x, 1};
    }
    sort(q + 1, q + tt + 1);
    int id = 1;
    for(int i = 1; i <= tt; i++) {
        while(id <= q[i].pos && id <= n) tr.add(a[id++], 1);
        ans[q[i].id] += q[i].sign * tr.ask(q[i].x);
    }
    for(int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}
posted @ 2024-07-31 11:56  Brilliant11001  阅读(14)  评论(0编辑  收藏  举报