前言:
这是Educational Codeforces Round 119 (Rated for Div. 2)的第五题,
这个题我最开始使用从后往前的方法写的,复杂度 O(n)O(n)O(n)。
交完以后我看看了题解,题解提供了两种办法,第二种是用启发式合并(英文名叫 small to large method),看到这我别提有多激动了,因为自从学了启发式合并以后一直没有找到适合练手的题目。
那我就通过这个题来好好梳理一下启发式合并吧。

例题题面
You have an array of integers (initially empty).

You have to perform q queries. Each query is of one of two types:

“1 x” — add the element x to the end of the array;
“2 x y” — replace all occurrences of x in the array with y.
Find the resulting array after performing all the queries.

Input
The first line contains a single integer q (1≤q≤5⋅105) — the number of queries.

Next q lines contain queries (one per line). Each query is of one of two types:

“1 x” (1≤x≤5⋅105);
“2 x y” (1≤x,y≤5⋅105).
It’s guaranteed that there is at least one query of the first type.

Output
In a single line, print k integers — the resulting array after performing all the queries, where k is the number of queries of the first type.

啥是启发式合并
启发式合并,就是高效的、从小到大的合并。它给人的直接感觉很弱,不像线段树什么的能让你瞬间体会到算法的精妙性。实际上,启发式合并可以将很多n2n^2n2的复杂度降级为nlognnlognnlogn,如果敏锐地洞察出一个题可以启发式合并,我们的算法性能将得到极大的提升。

启发式合并怎么做
以此题为例,我们为每个数开一个线性表(最好用vector)。执行操作111时,若x是第n个追加的元素,则将n放入第xxx个线性表(也就是说一个线性表里面装了这个值出现的所有位置)。执行操作222时,我们要将x出现的位置改为yyy,也就是要把第xxx个线性表全部倒入第yyy个线性表。如果暴力来处理的话,必定会超时。那么怎么优化呢?这里无非是对两个线性表的一个合并,我们利用启发式合并的策略,将较小的那个线性表倒入较大的线性表即可(此处的大小指元素个数)。合并完之后,如果表名不一致颠到,交换一下即可(vector的交换机制是改指针,所以复杂度是O(1)O(1)O(1)的)。需要注意的是,当x=yx=yx=y时,应当直接跳过。

启发式合并的复杂度
本算法的主要时间消耗在于合并两个线性表(或者说是集合)。
每次合并操作,我们都将小集合倒入大集合,只有小集合中的元素发生了移动,而且最终集合的大小至少是小集合的两倍。
假设aaa被移动了bbb次,也就是说aaa所在的集合的大小发生了b次倍增(有可能比倍增还大)。所以n>=2bn>=2^bn>=2b,也就是说b<=lognb<=lognb<=logn。
因此所有元素移动的总次数是nlognnlognnlogn。
仔细阅读我们发现,在推导这个复杂度的时候,我们分析的是移动次数最多的那个元素的最多移动次数,所以这个算法的效率往往比我们预期的还要快很多。

例题代码

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;vector<int>v[500010];
int a[500010];
signed main(){ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);int q,num=0;cin>>q;while(q--){int op,x,y;cin>>op;if(op==1){cin>>x;v[x].push_back(++num);}else{cin>>x>>y;if(x==y) continue;if(v[x].size()>v[y].size()){for(auto i:v[y]){v[x].push_back(i);}v[y].clear();swap(v[x],v[y]);}else{for(auto i:v[x]){v[y].push_back(i); }v[x].clear();}}} for(int i=1;i<=500000;i++){for(auto j:v[i]){a[j]=i;}}for(int i=1;i<=num;i++){cout<<a[i]<<' ';}cout<<endl;return 0;
}

启发式合并详解 (基于CF 1620E - Replace the Numbers)相关推荐

  1. A*启发式搜索算法详解 人工智能

    A*启发式搜索算法详解 人工智能 我们尝试解决的问题是把一个游戏对象(game object)从出发点移动到目的地.路径搜索(Pathfinding)的目标是找到一条好的路径--避免障碍物.敌人,并把 ...

  2. 详解基于 Cortex-M3 的任务调度(下)

    文章目录 工程说明 实验结果 代码讲解 时钟节拍 任务切换 task_switch() PendSV_Handler 任务的代码 重要的全局变量 main() 函数 代码下载 在 详解基于 Corte ...

  3. 《嵌入式Linux软硬件开发详解——基于S5PV210处理器》——2.2 DDR2 SDRAM芯片

    本节书摘来自异步社区<嵌入式Linux软硬件开发详解--基于S5PV210处理器>一书中的第2章,第2.2节,作者 刘龙,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  4. python selenium爬虫_详解基于python +Selenium的爬虫

    详解基于python +Selenium的爬虫 一.背景 1. Selenium Selenium 是一个用于web应用程序自动化测试的工具,直接运行在浏览器当中,支持chrome.firefox等主 ...

  5. Apollo进阶课程㉘丨Apollo控制技术详解——基于模型的控制方法

    原文链接:进阶课程㉘丨Apollo控制技术详解--基于模型的控制方法 PID控制是一个在工业控制应用中常见的反馈回路部件,由比例单元P.积分单元I和微分单元D组成.PID控制的基础是比例控制:积分控制 ...

  6. 《嵌入式Linux软硬件开发详解——基于S5PV210处理器》——1.2 S5PV210处理器

    本节书摘来自异步社区<嵌入式Linux软硬件开发详解--基于S5PV210处理器>一书中的第1章,第1.2节,作者 刘龙,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  7. php 六边形 属性图 能力数值图,详解基于 Canvas 手撸一个六边形能力图

    一.前言 六边形能力图如下,由 6 个 六边形组成,每一个顶点代表其在某一方面的能力.这篇文章我们就来看看如何基于 canvas 去绘制这么一个六边形能力图.当然,你也可以基于其他开源的 js 方案来 ...

  8. OSPFv2原理详解(基于RFC2328)+配置介绍+RFC2328翻译

    个人认为,理解报文就理解了协议.通过报文中的字段可以理解协议在交互过程中相关传递的信息,更加便于理解协议. 虽然路由器自身可以对协议做一些独特的配置,但是报文仍然是协议的核心.例如,OSPF的完全末节 ...

  9. 《嵌入式Linux软硬件开发详解——基于S5PV210处理器》——2.5 WM8960音频编解码芯片...

    本节书摘来自异步社区<嵌入式Linux软硬件开发详解--基于S5PV210处理器>一书中的第2章,第2.5节,作者 刘龙,更多章节内容可以访问云栖社区"异步社区"公众号 ...

最新文章

  1. 洛谷P1402 酒店之王(二分图)
  2. 大部分公司并不需要微服务
  3. 有服务器风扇声音对胎儿有影响吗,大噪音对胎儿的影响
  4. 院士袁亚湘:莫把数学当语文来教
  5. [BZOJ3173][Tjoi2013]最长上升子序列
  6. rabbitmq消息确认机制及死信队列的使用
  7. kotlin android 镜像,【54】Kotlin android Anko 神兵利器
  8. python爬虫实践
  9. C语言编译软件安装教程(Dev-C++版)
  10. springboot 整合 shiro (Web Applications)避坑一 ,请看shiro官网
  11. 北京科技大学计算机博士统考,北京科技大学计算机与通信工程学院2016年博士录取方案...
  12. linux 大黄蜂怎么安装视频教程,使用神舟K680E-G6D1在Deepin Linux系统下安装大黄蜂驱动的方法...
  13. YOLO系列梳理(九)初尝新鲜出炉的YOLOv6
  14. Windows + Linux 双系统 卸载 Liunx
  15. iOS各种证书配置总结
  16. uniapp + vue3微信小程序开发(2)活体人脸识别
  17. 基于MATLAB的分子动力学自编程序—300K下Ar的分子运动
  18. 服务器上tomcat修改内存,8g服务器tomcat内存设置
  19. 用公式π/ 4 = 1 – 1 / 3 + 1 / 5 – 1 / 7 + … 求π的近似值,直到最后一项的绝对值小于10的负6次方为止。
  20. 什么是DOM和BOM

热门文章

  1. 如何让图片放大不模糊 ?解决方法介绍
  2. 关于C盘重命名后恢复原用户的方法
  3. C# 注册DLL至GAC 并在添加引用中使用该DLL
  4. ObjectArx学习笔记
  5. WEBshell提权的20种思路
  6. 维修记录:东芝打印机2802am出现故障C449解决方法
  7. android afw模式,[HUAWEI-TITANC328]手机加密功能开启后,工作区中添加超级备份手机不断重启的分析报告...
  8. [省钱干货分享]24盘NAS硬盘柜只要400多?放卧室几乎没有声音?再次改造爱国者垃圾电源
  9. 从零开始完成Yolov5目标识别(一)准备工作
  10. python数据分析/机器学习 笔记之决策树(泰坦尼克号旅客生存预测)