IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 区间和 离散化入门与例题· C++ -> 正文阅读

[C++知识库]区间和 离散化入门与例题· C++

关于离散化,OIWIKI上是这么解释的:

????????通俗地讲就是当有些数据因为本身很大或者类型不支持,自身无法作为数组的下标来方便地处理,而影响最终结果的只有元素之间的相对大小关系时,我们可以将原来的数据按照从大到小编号来处理问题,即离散化。

用再通俗的话来讲,就是用下标替代原数值,解决一些特定问题;

什么样的特定问题呢?

影响结果的只有原数值的相对大小且原数值分布的比较稀疏(或者说差距比较大)

相对大小+差距大

比如给出3个坐标,1,100000,1000000000,其中每个坐标上分别对应一个权值a,b,c,其余没提到的坐标上对应的权值均是0;

那么如果我要求坐标区间内[l,r]的和,那么很显然,这是一个很明显的前缀和问题。

容易想到,我们创建一个数组,预处理后作一个前缀和数组,就可以解决问题了;

可是问题在于,当坐标过大的时候,数组没办法开那么大,即开头提到的:

自身无法作为数组的下标。

那么,于是,就有了离散化。

离散化本质上是一种hash

原因在于,我们可以把数值映射成坐标

即用坐标替代数值,数值的相对大小表现为坐标的相对大小

离散化操作通常需要先排序,然后去重,然后得到正确的映射。

所以,讲到这里,可以把离散化理解为一种映射(只不过需通过一些操作才能获得正确映射)。

以Acwing 802区间和为例,以经典例题的角度展开叙述:

802. 区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是?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

问题分析:

很显然,下标的大小达到了10的9次方,显然无法直接开等大的数组,但是我们可以离散化!

还是前面那句话!离散化就是把数值映射成下标。

????????那么可以看到,有n行输入,输入了n个坐标,和与其坐标对应的权值(应该累加的值);

那么考虑最坏的情况,如果这n个坐标互异,那么我们也仅仅需要开1e5大小的数组;

可以发现空间大幅度减小!

? ? ? ? 接下来的m行输入,每一行输入一对(l,r),也就是一对坐标x1,x2。由于我们最终要求[x1,x2]内的元素和,所以我们关心x1和x2上的权值,因而有必要在离散化的时候把x1,x2也进行一个映射。同样的,若这2m个坐标互异,2m<=2*1e5, 那么我们再开2*1e5大小的空间,也足以应付!(使得每个坐标都是一个唯一确定的下标与之对应)


因而,我们开一个坐标数组alls,对于每一个坐标,存入alls[i];

等价于alls[i]这个坐标映射成了下标i!

那么该如何实现这个操作?

很简单,我们先读入所有的坐标,push_back到alls数里面,然后sort一遍

就可以实现 若i<j,必定alls[i]<alls[j]的逻辑关系,即开头提到的用坐标替代数值,数值的相对大小表现为坐标的相对大小

然后我们需要去重,这一步可能比较突然,后面会解释。(其实当前可以这么理解,既然是映射,我们设坐标为x,x通过映射法则映射成f(x),那么根据数学知识,我们直到一个x仅且对应一个f(x),而一个f(x)是可以对应多个x)

所以目前,我们就形成了一个映射体系,即坐标和下标的一 一对应关系,用数组alls体现.

当坐标离散化后,由于我们想求的是元素和,那么需要关注权值;

权值仅出现在输入的时候,而我们希望把权值放到对应的坐标上,

换句话说,我们更希望,把权值放到对应的坐标所对应的离散化下标上。

但是,我们无法做到一边读入一边离散化,而我们上述的希望是基于离散化工作完成的基础上,那么,我们需要一个容器,暂时存下输入,vector<pair<int,int>> add,即操作向量

同样的,我们再准备一个操作向量,vector<pair<int,int>>query,暂时地把“询问”存起来。

当读入完毕后,我们就需要把权值放上去,因而离不开一个原数组(这里有暗示了,和前缀和相对应),我们创建它。

遍历add,每个元素的第一个位置存的是坐标,第二个元素存的是当前应该累加的值c;

那么该如何把坐标转化成对应的下标?由于alls这个坐标数组是排好序的且是一 一对应(这时候你就知道为什么要去重了),我们可以二分查找,找到坐标对应的下标i,执行a[i]++c;

那么,遍历完后,a[i]代表某个坐标上的权值(某个坐标:下标i相对应的坐标);

所以我们到这里就实现了,把权值放到对应的坐标所对应的离散化下标上。

那么接下来,原数组创建好了,下一步就是求终极任务:元素和

在原数组的基础上,我们开一个前缀和数组s(下标从1开始,避免特判)

那么s[i]=a[1]+a[2]+..a[i](1<2<...i,所对应的下标x1<x2<..xi)

s[i]可以表述为数轴上x1+x2+..xi的和

因而给定一组(i,j),我们列:s[i]-s[j-1] = a[j]+a[j+1]...a[i](每个元素都非0)(xj<xj+1<...xi)

表述为数轴上[xj,xi]的元素和,(非0的元素和+0的元素和)

这时候利用我们之前开的query数组,读入l,r;

由于l,r是真实的坐标,我们需要将其映射到下标,还是用到二分!

把[l,r]转化为[j,i],所以求xj+...xi = aj+...ai = s[i] - s[j-1],大功告成!

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

using namespace std;
const int N = 3e5+10;

int a[N],s[N];
typedef pair<int,int>PII;
vector<PII> add,query;
vector<int>alls;

int find(int 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;//找到第一个大于等于x的下标
}


int main()
{
    int n,m;
    cin >> n >> m;
    int x,c;
    for (int i = 1;i <= n;i++)
    {
        cin >> x >> c;
        alls.push_back(x);
        add.push_back({x,c});
    }
    int l,r;
    for (int i = 0;i<m;i++)
    {
        cin >> l >> r;
        query.push_back({l,r});
        alls.push_back(l);
        alls.push_back(r);
    }
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());
   for (auto i:add)
   {    
       int j = find(i.first);//使得下标x先映射到下标,再在下标数组中运算
       a[j]+=i.second;
   }
   for (int i = 1;i<=alls.size();i++) s[i] =s[i-1]+a[i];

   for (auto i:query)
   {
       int p = find(i.first),q = find(i.second);
       cout<<s[q] - s[p-1]<<endl;
   }
}

?

我是小郑,正在奔赴热爱,奔赴山海,体会数学和算法之美~???????

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:19:58  更:2022-06-14 22:20:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:02:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码