2022-11-07 Acwing每日一题

本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

今天来复习离散化的操作.

区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。
现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含两个整数 x 和 c。
再接下来 m 行,每行包含两个整数 l 和 r。
输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5

算法原理

离散化是建立了自然数到一段数列的映射(value->key),如将x,l,r都放进alls这个数组里,实现了数轴离散化成区间,重新建立了新的区间范围,从而能在更小的区间范围里去进行其他操作。
第一步是去重。将重复出现的地址数去除掉,并且进行排序(为了后面好二分)。
第二步是处理插入,即在alls中找到要进行操作的位置x,返回其在alls中的位置作为数组a的键key,并添加对应的值c(value)。
第三步就是获得离散化后的数列a的前缀和s
最后一步就是根据query获得查询区间的端点lr,利用前缀和公式即可得到这一段区间所有经过操作的数字之和。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
vector<PII> add,query;
vector<int> alls;   // 存储所有的地址,不仅存储了增加操作的地址还由输出区间的地址,将无穷长的数轴给他分割成有限区间
int n,m;
int a[1000010],s[1000010];  // a数组是数轴离散化后的数组,对离散化后的数组求前缀和即可得到这一区间的和

int find(int x){
    int l = 0,r = alls.size()-1;    // alls中包含了需要操作的所有区间,因此搜索范围就是alls内存储的地址
    while(l < r){
        int mid = (l+r) >> 1;
        if(alls[mid] >= x){
            r = mid;
        }
        else{
            l = mid+1;
        }
    }
    return l + 1;	// 因为考虑前缀和的边界,因此直接+1就不需要考虑这个了
}


int main(){
    cin >> n >> m;
    
    for(int i=0; i < n ; ++i){
        int x,c;
        cin >> x >> c;  // 输入在x处的值增加c
        add.push_back({x,c});   // 将这一对pair装进add中
        alls.push_back(x); // 将x装进alls中
    }
    
    for(int i=0; i<m ; i ++){
        int l ,r;
        cin >> l >> r;  // 输入查询区间
        query.push_back({l,r});     // 将区间pair放入query中
        alls.push_back(l);  // 再将其放入alls中
        alls.push_back(r);
    }
    
    sort(alls.begin(),alls.end());  // 将地址进行排序
    alls.erase(unique(alls.begin(),alls.end()),alls.end());  // 去掉重复的操作地址
    
    for(auto item : add){   // 处理插入
        int x = find(item.first);
        a[x] += item.second;    // a[x]连续的原因是因为alls中得下标是连续的,即使是插入也是在从0到alls.size()-1的区间内插入

    }
    // 区间求和
    for(int i=1; i <= alls.size() ; ++ i){
        s[i] = s[i-1] + a[i];
    }
    // 处理查询
    for(auto item:query){
        int l = find(item.first),r = find(item.second);
        cout << s[r] - s[l-1] << endl;
    }
    return 0;
}
posted @ 2022-11-07 17:09  ZmQmZa  阅读(12)  评论(0编辑  收藏  举报