二维数点问题 1

二维数点问题

例题 1

给定一个长为 nnn 的序列 aaa,有 mmm 次询问,每次询问给定 l,r,xl,r,xl,r,x,求 [l,r][l,r][l,r] 区间中小于等于 xxx 的数的个数。

数据范围:1≤n,m,ai,l,r,x≤2×1061 \le n,m,a_i,l,r,x\le 2\times 10^61n,m,ai,l,r,x2×106

题解

我们需要求出 l≤i≤rl\le i\le rlir 中,有多少个数小于等于 xxx,这里有 两个偏序关系 ≤\le≥\ge

将两个偏序关系减少至一个,就变成求出 i≤ri\le rir 有多少个数小于等于 xxx、求出 i≤l−1i\le l-1il1 有多少个数小于等于 xxx

将二者求出来的结果 作差 就得到了原需求。

所以目标转化为实现 i≤ki\le kik 有多少个数小于等于 xxx

我们将所有的询问都转为形如 (pos,x)(pos,x)(pos,x) 的二元组,并将其按 pospospos 排序。

1∼n1\sim n1n 开始遍历原序列,并维护一个权值树状数组,对于 a[i]a[i]a[i],单点插入树状数组中即可。

然后求 i≤posi\le posipos 有多少个数小于等于 xxx,就可以查询此时树状数组的 1∼x1\sim x1x 的前缀和。

现在我们求出了每一对二元组 (pos,x)(pos,x)(pos,x) 对应的答案,但是如何重组为 (l,r,x)(l,r,x)(l,r,x) 对应的答案呢?

有些题目可能会卡常,所以不能用 mapmapmap

可以考虑一个结构体 nodenodenode

struct node {
     int pos;
     int x;
     int id;
     int op;
};

nodenodenode 结构体是用来存放每一个询问的信息,其中的 ididid 表示是第几次询问,其中 opopop 表示符号。

因为一次询问 (l,r,x)(l,r,x)(l,r,x) 会被拆分为两个二元组 (l−1,x)(l-1,x)(l1,x)(r,x)(r,x)(r,x)

我们令 (l−1,x)(l-1,x)(l1,x)opopop−1-11,这样我们就可以将 (l−1,x)(l-1,x)(l1,x) 的结果乘上 opopop 累加到其对应的 ans[id]ans[id]ans[id] 中,表示减去,同理对于 (r,x)(r,x)(r,x)opopop111,表示加上。

#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define INF 1e18
const int N = 2e6;

template<typename T>

// 单点修改和区间查询的树状数组
struct Fenwick {
     vector <T> tree;
     int n;

     Fenwick(int _n) : n(_n), tree(_n + 2){}

     // 求 1~x 的前缀和
     T getsum(int x) {
          T ans = 0;
          int L = x;
          while (x) {
               ans += tree[x];
               x = x - lowbit(x);
          }
          return ans;
     }

     // 区间查询
     T get_range_sum(int l, int r) {
          return getsum(r) - getsum(l - 1);
     }

     // 单点修改
     void add(int x, T k) {
          while (x <= n) {
               tree[x] += k;
               x += lowbit(x);
          }
     }

     static inline int lowbit(int x) {
          return x & (-x);
     }
};

struct node {
     int pos;
     int x;
     int id;
     int op;
};


void slove () {
     int n, m;
     cin >> n >> m;

     vector <int> a (n + 1);
     for (int i = 1; i <= n; i++) cin >> a[i];


     vector <node> q;
     for (int i = 1; i <= m; i++) {
          int l, r, x;
          cin >> l >> r >> x;
          q.emplace_back(node{l - 1, x, i, -1});
          q.emplace_back(node{r, x, i, 1});
     }

     sort(q.begin(), q.end(), [](node A, node B) {
          return A.pos < B.pos;
     });

     Fenwick<int> tree(N);
     vector <int> ans(m + 1, 0);

     int L = 1, R = 0;
     while (R < q.size()) {
          auto now = q[R];
          while (L <= n && L <= now.pos) {
               tree.add(a[L], 1);
               L ++;
          }
          ans[now.id] += now.op * tree.get_range_sum(1, now.x);
          R ++;
     }

     for (int i = 1; i <= m; i ++) {
          cout << ans[i] << endl;
     }
}

signed main () {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    slove();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

louisdlee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值