【ACWing】802. 区间和

题目地址:

https://www.acwing.com/problem/content/description/804/

假定有个非常长的数轴,一开始每个坐标上的数都是 0 0 0。接着进行 n n n次操作,每次操作将某一位 x x x上的数加上 c c c。再接下来做 m m m次询问,每次询问区间 [ l , r ] [l,r] [l,r]所有数的和。

输入格式:
第一行包含两个整数 n n n m m m。接下来 n n n行,每行包含两个整数 x x x c c c。再接下里 m m m行,每行包含两个整数 l l l r r r

输出格式:
m m m行,每行输出一个询问中所求的区间内数字和。

数据范围:
− 1 0 9 ≤ x ≤ 1 0 9 -10^9\le x\le 10^9 109x109
1 ≤ n , m ≤ 1 0 5 1\le n,m\le 10^5 1n,m105
− 1 0 9 ≤ l ≤ r ≤ 1 0 9 -10^9\le l\le r\le 10^9 109lr109
− 10000 ≤ c ≤ 10000 -10000\le c\le 10000 10000c10000

求区间和可以用前缀和数组。但这里的主要问题是,如果我们开一个非常长的数组,然后统计每个位置被加了多少次,再算前缀和,这样非常浪费空间。我们可以采用离散化的方式来做。先将要用到的坐标存起来(显然每次操作的 x x x是要用到的,并且每次询问的 l l l r r r是要用到的。所以离散化后的数组要开长度为 n + 2 m n+2m n+2m即最大 300000 300000 300000),然后对其进行排序,排好序之后再去重(可能某个位置被加了多次,或者被询问多次)。接着进行操作,每次操作,就找到 x x x所在位置的下标(可以二分查找,因为要用到的下标都已经排好序了),然后在离散化后的数组 a a a中将那个位置增加 c c c;对于查询也是类似,查询开始之前先求一下 a a a的前缀和,然后先查一下 l l l r r r在离散化后的数组中的下标,接着根据前缀和数组来算区间和即可。代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;

const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;

// 返回x离散化之后的值
int find(int x) {
  int l = 0, r = alls.size() - 1;
  while (l < r) {
    int m = l + (r - l >> 1);
    if (alls[m] >= x) r = m;
    else l = m + 1;
  }

  return l + 1;
}

int main() {
  cin >> n >> m;
  for (int i = 0; i < n; i++) {
    int x, c;
    cin >> x >> c;
    add.push_back({x, c});
    alls.push_back(x);
  }

  for (int i = 0; i < m; i++) {
    int l, r;
    cin >> l >> r;
    query.push_back({l, r});
    alls.push_back(l);
    alls.push_back(r);
  }

  // 对alls做保序去重,以便做离散化
  sort(alls.begin(), alls.end());
  alls.erase(unique(alls.begin(), alls.end()), alls.end());

  // 执行加数操作
  for (auto item : add) {
    int x = find(item.x);
    a[x] += item.y;
  }

  // 求前缀和
  for (int i = 1; i <= alls.size(); i++) s[i] = s[i - 1] + a[i];

  for (auto item : query) {
    int l = find(item.x), r = find(item.y);
    cout << s[r] - s[l - 1] << endl;
  }
}

时间复杂度 O ( ( n + 2 m ) log ⁡ ( n + 2 m ) ) O((n+2m)\log (n+2m)) O((n+2m)log(n+2m)),空间 O ( n + 2 m ) O(n+2m) O(n+2m)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值