算法基础集训(第16天,共106天)------>彻底搞懂【离散化】类型的区间和问题,详细思路+题解+常见问题分析+万年无误代码模板

 

9ae00983ea59470d9c121dcc3ca4127d.gif

 

 

目录

 

一:概念定义

 

二:题目描述

 

三:思路解析 

 

四:万年无误代码模板 

 

五:常见问题 

 

六:相关前缀和以及二分的知识链接 

 


 

d4fcd5f72182485089a9b84328df2dd5.gif

 

一:概念定义

简单来说,就是把一些非常零散的点重新分配到从1~n的自然数,这个分配过程也可以称作映射,比如坐标轴范围是-10^9~10^9,但是其中有实际数值的坐标只有十个,我们把这个十个数字映射到1~10即可。

我们用一幅图来描述一下,这是个无限宽度的坐标轴:cc41832394554665bf4c9628d9d8651c.png 

 坐标点的数字没有规律,如果要我们求某一个区间的和,我们容易联想到前缀和,但是这个区间太宽,而且其中有很多坐标的数值是0,没有必要参与前缀和的过程,因此我们需要映射,拿上图举例子:074f992b278240d492b6ec4aca2069f4.png

 原本我们求的【-47,96】的区间和就转化为【1,10】的映射区间和,效率大幅提升。


二:题目描述

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

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

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

输入格式

第一行包含两个整数 n 和 m。

接下来 n 行,每行包含两个整数 x 和 c。

再接下来 m 行,每行包含两个整数 l 和 r。

输出格式

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

数据范围

 

−10^9 ≤ x ≤ 10^9,
1 ≤ n,m ≤ 10^5,
−10^9 ≤ l ≤ r ≤ 10^9
−10000≤c≤10000

输入样例

3 3

1 2

3 6 

7 5

1 3

4 6

7 8

输出样例 

8

0

5


三:思路解析 

首先,输入数据很多是成双出现,因此我们可以用pair结构来存储

整体大方向:去重+映射+前缀和


四:万年无误代码模板 

 

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

using namespace std;

typedef pair<int, int> PII;

const int N = 300010;//输入由是十万个坐标,检测l有十万个,r有十万个,因此我们将N开到三十万+10

int n, m;
int a[N], sum[N];

vector<int> all;
vector<PII> add, query;

int find(int x)//find函数用来寻找x在所有输入坐标的排名(映射)
{
    int l = 0, r = all.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (all[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;//r+1是因为映射一般从1开始所以加1,因为前缀和习惯从1开始
}

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

        all.push_back(x);
    }

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

        all.push_back(l);
        all.push_back(r);
    }

    // 去重
    sort(all.begin(), all.end());
    all.erase(unique(all.begin(), all.end()), all.end());

    // 处理插入
    for (auto i : add)
    {
        int x = find(i.first);
        a[x] += i.second; //求出映射从1~n的数值,i.second就是题目中的c
    }

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

    // 简单输出
    for (auto i: query)
    {
        int l = find(i.first), r = find(i.second);
        cout << sum[r] - sum[l - 1] << endl;
    }

    return 0;
}

五:常见问题 

1.为什么要去重?

2.为什么要把l和r也加入到all中?

3.去重函数怎么写?

 

1. 其实不用去重也不影响代码实现,但是去重之后就会很严格的映射1~n,并且是一对一的关系,我们的find函数(其实就是二分查找)是查找x的映射,而且是第一个大于等于x的值,本体中其实最后find返回的就是被查找值x的下标

2.比如要求【4,200】这段区间和,假设其映射是【3,9】,我们需要通过s【9】-s【2】来得出答案,如果不输入l,r,就得不到其映射,就无法通过前缀和来得到答案

3.首先,unique函数是返回第一个重复数字的地址,将不重复的数字放到前面,erase实现重复数字到最后数字的去除工作


六:相关前缀和以及二分的知识链接 

前缀和入门

前缀和值子矩阵的和

二分详解

创作不易,建议点赞+收藏+关注,以免变成付费资源或者找不到宝贝文章了。

基础集训结束后将开展拔高系列

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码kobe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值