离散化题型:区间和

本文介绍了如何在数据范围巨大的情况下,通过离散化技术将区间范围缩小,从而高效地进行插入和区间求和操作。作者提供了C++代码示例,展示了如何使用离散化方法解决大区间问题,以避免内存限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目如下:

首先通过读题我们很容易知道本题就是一个插入数据以及区间求和的题目,粗略读完题的第一反应一般都是前缀和直接秒了(doge)但是通过观察数据范围我们发现坐标轴的区间非常大远远超过了1e5(此范围一般是应用前缀和的范围),这时就引出了本题的类型——离散化。

离散化的定义我们可以认为是把一个范围非常大的且数据分布稀疏的区间(取其精华去其糟粕)浓缩成一段较短且数据密集的区间,在这段小区间进行操作的过程。即化大为小

例如我们这道题,给定的区间初始范围是10的负9次方到10的9次方,远远超过了我们平常所用到的区间范围,如果你放在main()函数里开会爆种,如果你放在全局变量开数组虽然理论上可以,但是你的内存——危!所以,我们继续观察题目的话会发现实际上我能用到的区间完全是取决于n和m的大小,也就是说我只需要开辟一个数组来使我能最大限度的使用完插入数据和求区间和的操作就行,那么由于插入操作包含一个原区间坐标x,求区间和操作包含两个原区间坐标l和r。所以我们新数组的最大范围应该是要>=n+2m即大于等于300000。

现在知道什么是离散化以及为何要用离散化解题了,那下面我们就看一下具体代码怎么写吧。

代码展示:

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

using namespace std;


typedef pair<int, int> PII;
const int N=300005;
int a[N],s[N];//a[]用来存储离散化后坐标对应的数值,s[]存储前缀和
vector<PII>add,pre;//add存储插入操作的下标和c,pre存储求区间和的左右边界
vector<int>alls;//用来存储待离散化的操作的坐标


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;//返回r+1主要是为了后面的预处理以及求前缀和操作
}


int main(void)
{
    int n,m;
    cin>>n>>m;//输入要执行增添数字操作以及求区间和的操作的次数

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

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


    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());//去重操作

    //这里去重的原因在于我们要保证后续进行插入和求和操作时,alls数组中的数据有且仅有唯一值能够与add,pre操作的坐标对应,是为求前缀和打下的坚实的基础!
    //另外要注意,在去重之前需要将待去重数组排序!不然你没法去重呀(不知道为啥的建议了解一下unique的去重机制)

    //处理插入操作
    for(auto item:add)
    {
        int x=find(item.first);

//当我们在调用find函数查找add里的坐标item.first在离散化数组的位置后,通过返回的x可以在a[]中进行相应的插入或求和操作
        a[x]+=item.second;
    }


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

//这里注意i=1以及i<=alls.size()正好与我们前面find函数的返回值首尾呼应,保证了前缀和的起始下标为1.


    //处理区间求和操作
    for(auto item:pre)
    {
        int l=find(item.first);
        int r=find(item.second);
        cout<<s[r]-s[l-1];
        cout<<endl;
    }
    return 0;
}

鉴于第一次写很多地方语言组织不到位,理解可能还不够透彻,希望大家多多体谅。文章没有配置图解需要大家自己动手画画,最后祝愿大家未来天天AC!欢迎大家多多批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值