AcWing 802. 区间和

题目

假定有一个无限长的数轴,数轴上每个坐标上的数都是 \(0\)

现在,我们首先进行 \(n\) 次操作,每次操作将某一位置 \(x\) 上的数加 \(c\)

接下来,进行 \(m\) 次询问,每个询问包含两个整数 \(l\)\(r\),你需要求出在区间 \([l, r]\) 之间的所有数的和。

输入格式

第一行包含两个整数 \(n\)\(m\)

接下来 \(n\) 行,每行包含两个整数 \(x\)\(c\)

再接下来 \(m\) 行,每行包含两个整数 \(l\)\(r\)

输出格式

\(m\) 行,每行输出一个询问中所求的区间内数字和。

数据范围

\(-10^9 \le x \le 10^9\),
\(1 \le n,m \le 10^5\),
\(-10^9 \le l \le r \le 10^9\),
\(-10000 \le c \le 10000\)

输入样例:

3 3
1 2
3 6
7 5
1 3
4 6
7 8

输出样例:

8
0
5

题解

C++ 代码

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

using namespace std;

typedef pair<int, int> PII; //给可存储两个数据的数据类型 pair<int, int> 起别名 PII

const int N = 3e5 + 10; //n次插入(使用l和r两个坐标)和m次查询(使用x一个坐标)相关数据量的上界  一个坐标数据上限是1e5

int n, m;
int a[N], s[N]; //a[]存储做表插入的值  s[]存储a[]前缀和

vector<int> alls; //存储(所有与插入和查询有关的)坐标
vector<PII> add, query;  ////存储插入和询问操作的数据

int find(int x)   //运用二分的思想  返回的是输入的原坐标x的离散化下标
{                 //其实质是将一个很大的数据范围映射到从l到r = alls.size - 1 的一个小区间内
    int l = 0, r = alls.size() - 1;   //由于所有用到的下标个数就是插入和询问的次数 所以远小于原来全部坐标的数据范围 离散化的本质就是只需处理排序我们需要用到的坐标
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;   //alls[mid]是原坐标 mid是原坐标在数组中的位置 例如原坐标1 3 5 7 9 在数组中的位置是1 2 3 4 5,根据数组中的位置可形成索引,从而映射出新坐标
        else l = mid + 1;
    }
    return r + 1;   //确保离散化下标的值从1开始
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ )
    {
        int x, c;
        scanf("%d%d", &x, &c);
        add.push_back({x, c}); //记录插入的原坐标和值
        alls.push_back(x);  //记录插入的原坐标
    }
    for (int i = 0; i < m; i ++ )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r}); //记录询问的坐标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) //执行前n次插入操作
    {
        int x = find(item.first);  //item.first 是add存储的第一个元素即x 原坐标 ,x = find(x)将其转化为离散化下标
        a[x] += item.second;  //item.second 是add存储的第二个元素即c 此时执行插入数据的操作
    }

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

    for (auto item : query)  //执行m次询问操作
    {
        int l = find(item.first);
        int r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}
posted @ 2024-04-16 17:47  MsEEi  阅读(16)  评论(0)    收藏  举报