基础算法入门09——离散化

区间和(离散化)

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

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

接下来,进行 m m m 次询问,每个询问包含两个整数 l l l r r r,你需要求出在区间 [ 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

输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5

算法:离散化

数据量少,但是数据量差异大,可以考虑进行离散化。

可能会输入a[4]=10,a[6]=23,a[1000]=500000;

像这样,数据间隔比较大的话,不妨采用离散化。

建立新的离散化的数组。

b[0]=4,b[1]=23,b[2]=500000;

当我们要查询某一段数据和的时候,就需要先对查询数据的下标进行离散化,得到b数组中的下标。

注意:我们要离散化的对象是下标

我们为离散化对象创建一个新的数组indexes[],这个数组的下标是连续的,这些下标就是原来不连续的下标的映射,也就是他们的离散值。

关于找到原来不连续的下标对应的离散值,采取对indexes[]进行二分查找的操作。

而对于原本不连续数组下标对应元素的操作就转换成对离散值对应元素的操作。

image-20220327011632354

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=300010;
int a[N],s[N];//分别用来存储数轴上的数,及前缀和
vector<PII> adds,query;//分别存储题目中的下标x的数进行+c操作和查询操作
vector<int> indexes;//存储所有操作涉及到的下标,对这些不连续下标进行离散化
int find(int x)//简单的二分查找,寻找元素对应的下标
{
    int n=indexes.size();
    int l=0,r=n-1;
    while(l<r)
    {
        int mid=l+r>>1;
        if(indexes[mid]>=x) r=mid;
        else l=mid+1;
    }
    return l+1;
}
int main()
{
    int n,m;
    cin>>n>>m;
    while (n -- )
    {
        int x,c;
        cin>>x>>c;
        indexes.push_back(x);//存储adds操作相关下标
        adds.push_back({x,c});//记录adds操作
    }
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        indexes.push_back(l);
        indexes.push_back(r);//存储query操作相关下标
        query.push_back({l,r});//记录query操作
    }
    //进行离散化
    sort(indexes.begin(),indexes.end());
    indexes.erase(unique(indexes.begin(),indexes.end()),indexes.end());//先排序,在去重;
    
    for(auto ad:adds)//进行加c操作
    {
        int x=find(ad.first);//找到原下标对应的离散值
        int value=ad.second;//存储adds操作的value
        a[x]+=value;//在一个新的连续数组上存储原不连续数组元素的值
    }
    
    for(int i=1;i<=indexes.size();i++)//求前缀和
    {
        s[i]=s[i-1]+a[i];
    }
    
    for(auto q:query)
    {
        int l=find(q.first);//将查询的区间转化为对应的离散化之后的区间
        int r=find(q.second);
        cout<<s[r]-s[l-1]<<endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值