首页 > 其他分享 >OI-tips 离散化

OI-tips 离散化

时间:2024-08-18 19:26:18浏览次数:12  
标签:map OI int 1000005 离散 数组 mathcal tips

离散化

离散化,一个很简单却很常用的小技巧。

引入

给你 \(n\) 个数,输出每种数出现的个数,满足 \(n\le 10^6\),\(a_i\le 2\times 10^9\)。

是不是看上去很简单,直接开一个数组记录个数不就行了。

不过值域的范围是 \(2\times 10^9\),显然正常开数组的话我们是开不下的,而发现 \(n\) 是很小的,在我们可支持的范围内,于是就有了一个映射的思想:我们给每个数标号,记录编号出现的个数,这样就可以将空间复杂度控制在 \(\mathcal{O(n)}\) 级别。这种重新赋一个较小的值的思想就是离散化。

实现

理解了思想,我们再去考虑如何实现。

map 实现

STL 容器中的 map 是蒟蒻们实现离散化的好帮手,它出现的意义本身就是映射:将某种类型的某值映射到某种类型的某值上。

上面提到的“某种类型”可以是 c++ 中的任意数据类型,当然最常用的就是 int 转到 int,即我们的离散化操作。

关于 map 容器我浅讲一点,有兴趣可以自行学习。

定义方法

map 位于 std 库中,若没有在前面声明 using namespace std,需要这样定义(下面均为未声明情况):

std::map<int,int>mp;
// </*原数据类型*/,/*映射后数据类型*/>

使用方法

很简单,就和普通的数组一样。

int a=1000000000;
mp[a]=1;
// 将 a 的值映射为 1

例题实现

#include<bits/stdc++.h>
int n,tot,a[1000005],sum[1000005];
bool vis[1000005];
std::map<int,int>mp;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(!mp[a[i]]) mp[a[i]]=++tot;
		// 离散化
		sum[mp[a[i]]]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[mp[a[i]]]) continue;
		vis[mp[a[i]]]=1;
		printf("%d %d\n",a[i],sum[mp[a[i]]]);
	}
	return 0;
}

优化

可以将 map 换成 unordered_map,会有一点加速效果。

数组实现

众所周知,stl 容器的速度都比较玄学,因此在面对需要大量运用离散化时,我们需要一种较稳定的实现方法。

回归主题,我们思考一下,离散化的本质是什么?是去重。stl 算法中存在一个操作 unique,作用是去除容器中相邻的重复元素,好像跟我们的目的很像,但发现其去重的对象只是相邻元素,于是我们在去重前需要先将原序列排序。这就有了离散化=排序+去重的说法。

这样离散化后的数据还能反映数值的大小关系,这就大大增强了这种方法的泛用性。

注意: 由于我们改变了序列的顺序,我们获取每个元素离散化后的值时需要用到 lower_bound 操作,如果原来元素的值对后续没有影响,这个操作最好在离散化后立即进行。(不然像我一样用的时候再查就直接给复杂度乘了一个 \(\log\))

for(int i=1;i<=n;i++) b[i]=a[i];
tot=n;
sort(b+1,b+1+tot);
tot=unique(b+1,b+1+tot)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+tot,a[i])-b;

例题实现

#include<bits/stdc++.h>
int a[1000005],b[1000005],num[1000005],n,tot;
bool vis[1000005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
	tot=n;
	sort(b+1,b+1+tot);
	// 先排序
	tot=unique(b+1,b+1+tot)-b-1;
	// 再去重,tot 直接赋值为不重复的数的个数
	for(int i=1;i<=n;i++)
	{
		int id=lower_bound(b+1,b+1+tot,a[i])-b;
		num[id]++;
	}
	for(int i=1;i<=n;i++)
	{
		int id=lower_bound(b+1,b+1+tot,a[i])-b;
		if(vis[id]) continue;
		vis[id]=1;
		printf("%d %d\n",a[i],num[id]);
	}
	return 0;
}

离散化虽然基础,但细节也挺多的。

操作难度 时间复杂度 空间复杂度
map 简单 \(\mathcal{O(n\log n)}\)(仅离散化) -
数组 不难 \(\mathcal{O(n\log n)}\)(稳定) \(\mathcal{O(n)}\)

不过实测下来还是数组更快一点,因为每次引用 map 都是 \(\mathcal{O(\log n)}\) 的,多几次就慢了。

本文代码全部线上手打,有问题请指出,感谢支持。


完结撒花~

标签:map,OI,int,1000005,离散,数组,mathcal,tips
From: https://www.cnblogs.com/Ratio-Yinyue1007/p/18365887

相关文章

  • 洛谷P1083 [NOIP2012 提高组] 借教室 && 差分学习笔记
    传送门:P1083[NOIP2012提高组]借教室"八骏日行三万里,穆王何事不重来。"可惜啊,他再也没有回来……题目大意:给你每天能够租借的教室数量和几份租借申请每份申请包含租界时间(从第几天到第几天)和每天需要租借的教室数量问你能否满足所有的租借要求,如果不能,驳回一份最前......
  • [Tkey] [IOI 2018] werewolf
    注意看,我耗时五个小时AK了IOI题意给你一个图,每次给定若干询问\((s,t,l,r)\),请你完成下述要求:定义\(S\)为到\(s\)的最短路径不小于\(l\)的点构成的子图,\(T\)为到\(t\)的最短路径不大于\(r\)的点构成的子图请你判断\(S\)与\(T\)是否有交集解法当询问次数......
  • OpenCV(logPolar()、Point2f())
    目录1.cv::logPolar()函数原型:参数说明:用途和示例:2.cv::Point2f类定义:属性:主要构造函数:用途和示例:总结:1.cv::logPolar()cv::logPolar()是OpenCV中用于进行对数极坐标变换(Log-PolarTransformation)的函数。对数极坐标变换将图像的空间坐标转换为极坐标,并对径向分量取对数......
  • P1002 [NOIP2002 普及组] 过河卒
    题目描述棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐......
  • P4689 [Ynoi2016] 这是我自己的发明 与 P5268 [SNOI2017] 一个简单的询问0
    思路:首先可以先考虑没有换根的情况。先将树拍到dfn序上,那么一个子树\(u\)的所有点的dfn序区间为\([dfn_u,dfn_u+siz_u-1]\)。那么询问变为:每次给定两个区间\([l_1,r_1],[l_2,r_2]\),对于在第一个区间内的点\(x\)和在第二个区间的点\(y\),若\((x,y)\)有贡献,当且仅......
  • P2048 [NOI2010] 超级钢琴
    题意在一个数组中选择\(k\)个长度为\([l,r]\)的序列,对每个序列求和,使每个序列的和的和最大。思路首先,我们可以将序列之和转化为前缀和,如果固定左端点\(l\),那么我们只需要在\([l+len_l,l+len_r]\)中寻找最大的右端点,减去\(sum[l-1]\)就是在长度为\([len_l,le......
  • 高德地图SDK Android版开发 6 显示覆盖物
    高德地图SDKAndroid版开发6显示覆盖物前言地图类中覆盖物的接口覆盖物类Marker示例Polyline示例Polygon示例Arc示例Circle示例移除示例效果图Marker的更多属性常用属性交互动画其它属性折线的更多属性常用属性其它属性多边形的更多属性常用属性其它属性Arc的更多......
  • 【Android驱动12】Modem编译和sim卡配置检测过程
    一,Modem编译1.1查看ReleseNote发现需要查看"Build_Configure_Modem_MOLY"这张表,解压MT67xx_(xxx)_MOLY.LR9.W1444.MD.LWTG.MP.Vx.tar.gz到某文件,并在make目录下查看支持的配置信息1.2执行的命令,开始编译modem,则是./make.sh"SM67xx(Lxx_xxx).mak"new1.3执行perl......
  • Android 13.0 recovery页面旋转180度问题的解决方案
    1.前言在13.0的系统rom定制化开发工作中,在系统中recovery的页面也是相关重要的一部分,在系统recoveryota升级等功能,都是需要recovery功能的,在某些产品定制化中在recovery的时候,发现居然旋转了180度,接下来分析下recovery关于屏幕显示方向的相关源码,来修改这个功能2.recovery......
  • 基于flask+vue框架的基于Android的校园公益管理APP小程序端[开题+论文+程序]-计算机毕
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景在当今社会,随着教育理念的进步和青年学生社会责任感的增强,校园公益活动已成为培养学生综合素质、促进社会和谐的重要途径。然而,传统的公益......