【算法】莫队

这篇博客起源于本人把一道 p o w ( 2 , n ) pow(2,n) pow(2,n) 的问题考虑成求组合数前缀和的问题qwq,于是接触到了这个新算法来总结一下

参考自这篇文章,写得太好了

首先是一道模板题

题目意思是,给出一个数组a,再给出多个区间,问这些区间里分别有多少不一样的数字

焗个栗子:
在这里插入图片描述
比如给出的是这样一个数组,询问的两个区间是红色和绿色标注的区间

如果我们分别遍历每一个区间,复杂度就太高了,因此就有大佬提出这样一种算法——

首先定义双指针 l l l r r r,初始化为 l = 0 , r = − 1 l=0,r=-1 l=0,r=1(表示此时区间内没有任何元素),然后用unorderd_map<int, int> map存储当前区间内每个元素的个数

看我们要求的第一个区间 [ 0 , 5 ] [0,5] [0,5]

l l l 此时等于 0 0 0,和所求区间的左端点相同,因此无需移动

r r r 此时等于 − 1 -1 1,在所求区间右端点的左侧,因此需要向右移

首先向右移一位变成 0 0 0,此时区间内添加了一个元素 5,因为map[5] = 0,元素5不存在于原来的区间里,因此元素种类数cnt加一,map[5] ++

然后 r r r 再右移一位变成 1,此时区间内添加了一个元素 7,因为map[7] = 0,元素7不存在于原来的区间里,因此元素种类数cnt加一,map[7] ++

依此类推,直到 r r r 右移到了 5,此时区间内添加了一个元素 7,但是添加前的map[7] = 1,添加了当前的 7 并不会让区间内元素的个数增多,因此cnt无需改变,map[7] ++

之后从红色区间挪到绿色区间的操作也是类似的

上文介绍了如何往区间内添加数,当然,删除一个数的操作也是类似的,只不过添加数时,我们要先看被添加的数有没有出现在原来的区间里,也就是map[x]是否等于0,如果等于0说明不存在于原来的区间,区间内元素种类数cnt才能加一,而删除数时,我们要先把当前数删除,也就是map[x]减一,然后再判断当前区间里还有没有x,如果没有x也就是map[x] == 0,那么区间内元素种类数cnt减一

code

// 添加数
void add(int pos)
{if (!map[a[pos]]) cnt ++ ; // 在区间中新出现,总数要+1map[a[pos]] ++ ;
}// 删除数
void del(int pos)
{map[a[pos]] -- ;if (!map[a[pos]]) cnt -- ; // 在区间中不再出现,总数要-1
}int main()
{for (int i = 1; i <= q; ++i){// 输入询问区间的左右端点int ql, qr;cin >> ql >> qr;while (l < ql) del(l++); // 如左指针在查询区间左方,左指针向右移直到与查询区间左端点重合while (l > ql) add(--l); // 如左指针在查询区间左端点右方,左指针左移while (r < qr) add(++r); // 右指针在查询区间右端点左方,右指针右移while (r > qr) del(r--);        // 否则左移cout << cnt;}
}

然后就 结束啦 qwq

才怪

还可以对这个算法继续优化

莫队算法优化的核心是分块排序

把长度为 n n n 的序列分成 n \sqrt{n} n 个块,然后按照左端点所在的块排序,如果左端点在同一个块内,则按右端点排序

优化1
这种排序方法也可以接着优化:如果左端点在同一奇数块内,则按右端点从小到大排序,如果左端点在同一偶数块内,则按右端点从大到小排序,这样排序是为了减少右端点移动的次数进而提高效率)

优化2
听说开O2优化能产生奇妙反应,实在没办法了可以考虑考虑这个

#pragma GCC optimize(2)

优化3
某佬把上面的一长串代码简化成了下面这样

while(l < ql) cnt -= !--map[a[l++]];
while(l > ql) cnt += !map[a[--l]]++;
while(r < qr) cnt += !map[a[++r]]++;
while(r > qr) cnt -= !--map[a[r--]];

于是模板题的代码如下

#include <bits/stdc++.h>using namespace std;typedef pair<pair<int, int>, int> PPI;int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int n;cin >> n;int size = sqrt(n);int bnum = ceil((double)n / size); // 把序列分成了多少块vector<int> belong(1e6 + 10);for (int i = 1; i <= bnum; i ++ )for (int j = (i - 1) * size + 1; j <= i * size; j ++ ){belong[j] = i; // 标记每个元素属于哪个块}vector<int> a(n + 1);for (int i = 1; i <= n; i ++ ) cin >> a[i];int m;cin >> m;vector<PPI> q(m);for (int i = 0; i < m; i ++ ){cin >> q[i].first.first >> q[i].first.second;q[i].second = i;}function<bool(PPI a, PPI b)> cmp = [&](PPI a, PPI b){// return (belong[a.first.first] ^ belong[b.first.first]) ? belong[a.first.first] < belong[b.first.first] : ((belong[a.first.first] & 1) ? a.first.second < b.first.second : a.first.second > b.first.second);if (belong[a.first.first] != belong[b.first.first]) return belong[a.first.first] < belong[b.first.first];else{if (belong[a.first.first] % 2 == 1) return belong[a.first.second] < belong[b.first.second];else return belong[a.first.second] > belong[b.first.second];}};sort(q.begin(), q.end(), cmp);int l = 1, r = 0, cnt = 0;unordered_map<int, int> map;vector<int> ans(m);for (int i = 0; i < m; i ++ ){int ql = q[i].first.first, qr = q[i].first.second;while(l < ql) cnt -= !--map[a[l ++ ]];while(l > ql) cnt += !map[a[-- l]] ++;while(r < qr) cnt += !map[a[++ r]] ++;while(r > qr) cnt -= !--map[a[r -- ]];ans[q[i].second] = cnt;}for (int i = 0; i < m; i ++ ) cout << ans[i] << '\n';
}

剩下的之后更新~~qwq

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/91323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无人直播间

失败&#xff01;&#xff01; 采用 ffmpeg 技术进行推流 推流代码&#xff1a; 【需要将rtmp替换为你的推流地址】 ffmpeg -re -stream_loop -1 -i "rain.mp4" -c copy -f flv ""推流地址获取 以哔哩哔哩为例 点击下方链接 开播设置 - 个人中心 - …

【MATLAB源码-第39期】基于m序列/gold序列的直接扩频通信仿真,编码方式采用卷积码,调制方式采用BPSK。

1、算法描述 直接序列扩频通信系统的仿真一般包括以下几个主要步骤&#xff1a;信号产生、扩频、卷积编码、BPSK调制、信道传输、BPSK解调、卷积码译码和解扩。 信号产生&#xff1a; 首先&#xff0c;产生一个二进制数据序列作为待发送的信息位。 扩频&#xff1a; 采用m序列…

如何开始着手一篇Meta分析 | Meta分析的流程及方法

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

十五、异常(3)

本章概要 捕获所有异常 多重捕获栈轨迹重新抛出异常精准的重新抛出异常异常链 捕获所有异常 可以只写一个异常处理程序来捕获所有类型的异常。通过捕获异常类型的基类 Exception&#xff0c;就可以做到这一点&#xff08;事实上还有其他的基类&#xff0c;但 Exception 是所…

鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统

项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管理的提升提出了更高的要求。 二、企业通过数字化转型&#xff0c;不仅有利于优化业务流程、提升经营管理…

HTML——列表,表格,表单内容的讲解

文章目录 一、列表1.1无序&#xff08;unorder&#xff09;列表1.2 有序&#xff08;order&#xff09;列表1.3 定义列表 二、表格**2.1 基本的表格标签2.2 演示 三、表单3.1 form元素3.2 input元素3.2.1 单选按钮 3.3 selcet元素 基础部分点击&#xff1a; web基础 一、列表 …

【JavaEE】CAS(Compare And Swap)操作

文章目录 什么是 CASCAS 的应用如何使用 CAS 操作实现自旋锁CAS 的 ABA 问题CAS 相关面试题 什么是 CAS CAS&#xff08;Compare and Swap&#xff09;是一种原子操作&#xff0c;用于在无锁情况下保证数据一致性的问题。它包含三个操作数——内存位置、预期原值及更新值。在执…

轻量自高斯注意力(LSGA)机制

light&#xff08;轻量&#xff09;Self-Gaussian-Attention vision transformer&#xff08;高斯自注意力视觉transformer&#xff09; for hyperspectral image classification&#xff08;高光谱图像分类&#xff09; 论文&#xff1a;Light Self-Gaussian-Attention Vision…

完整指南:如何使用 Node.js 复制文件

文件拷贝指的是将一个文件的数据复制到另一个文件中&#xff0c;使目标文件与源文件内容一致。Node.js 提供了文件系统模块 fs&#xff0c;通过该模块可以访问文件系统&#xff0c;实现文件操作&#xff0c;包括拷贝文件。 Node.js 中文件拷贝方法 在 Node.js 中&#xff0c;有…

基于微信小程序的宠物寄养平台小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

预编译(1)

目录 预定义符号&#xff1a; 使用&#xff1a; 结果&#xff1a; 预编译前后对比&#xff1a; #define定义常量&#xff1a; 基本语法&#xff1a; 举例1&#xff1a; 结果&#xff1a; 预编译前后对比&#xff1a; 举例2&#xff1a; 预编译前后对比&#xff1a; 注…

ELK介绍

一、前言 前面的章节我们介绍通过ES Client将数据同步到ElasticSearch中&#xff0c;但是像日志这种数据没有必要自己写代码同步到ES那样会折腾死&#xff0c;直接采用ELK方案就好&#xff0c;ELK是Elasticsearch、Logstash、Kibana三款开源软件的缩写&#xff0c;ELK主要用于…

P2PNet-Soy原理梳理

前文总结了P2PNet源码以及P2PNet-Soy源码实现方法&#xff0c;相关链接如下&#xff1a; 人群计数P2PNet论文&#xff1a;[2107.12746] Rethinking Counting and Localization in Crowds:A Purely Point-Based Framework (arxiv.org) p2p人群计数源码&#xff1a;GitHub - Te…

云服务器租用价格表概览_阿里云腾讯云华为云

云服务器租用价格多少钱一年&#xff1f;阿腾云分享阿里云、腾讯云和华为云的云服务器租用价格表&#xff1a;阿里云2核2G服务器108元一年起、腾讯云2核2G3M带宽轻量服务器95元一年、华为云2核2G3M云耀L实例89元一年起&#xff0c;阿腾云分享更多关于云服务器租用价格明细&…

Kubernetes基础(五)-Service

1 引言 Service 主要用于提供网络服务&#xff0c;通过Servicel的定义&#xff0c;能够 为客户端应用提供稳定的访问地址&#xff08;域名或IP地址&#xff09;和负载均衡功能&#xff0c;以及屏蔽后端Endpoint的变化&#xff0c;是Kubernetes实现微服务的核心资源。 本文详细…

博弈论中静态博弈经典场景案例

博弈论中静态博弈经典场景案例 1、齐威王田忌赛马 田忌赛马是中国家喻户晓的故事&#xff0c;故事讲述的是齐国大将田忌的谋士孙膑如何运用计谋帮助田忌在与齐威王赛马时以弱胜强的故事&#xff0c;这个故事其实本质也是一个博弈的过程。     齐威王要和田忌赛马&#xff…

二叉树MFC实现

设有一颗二叉树如下&#xff1b; 这似乎是一颗经常用作示例的二叉树&#xff1b; 对树进行遍历的结果是&#xff0c; 先序为&#xff1a;3、2、2、3、8、6、5、4&#xff0c; 中序为&#xff1a;2、2、3、3、4、5、6、8&#xff0c; 后序为2、3、2、4、5、6、8、3&#xff1b…

MySQL学习笔记25

逻辑备份 物理备份 在线热备&#xff1a; 真实案例&#xff1a; 数据库架构是一主两从&#xff0c;但是两台从数据库和主数据不同步。但是每天会全库备份主服务器上的数据到从服务器上。需要解决主从不同步的问题。 案例背后的核心技术&#xff1a; 1、熟悉MySQL数据库常见…

【计算机视觉|人脸建模】PanoHead:360度几何感知的3D全头合成

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{\circ} ∘ 链接&#xff1a;[2303.13071] PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{\circ} ∘ (arx…

大数据Doris(三):Doris编译部署篇

文章目录 Doris编译部署篇 一、Doris编译