【算法】莫队

news2024/11/23 4:55:10

这篇博客起源于本人把一道 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 ++ ; // 在区间中新出现,总数要+1
    map[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.coloradmin.cn/o/1051396.html

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

相关文章

What is an HTTP Flood DDoS attack?

HTTP 洪水攻击是一种针对 Web 和应用程序服务器的第 7 层分布式拒绝服务 &#xff08;DDoS&#xff09; 攻击。HTTP 洪水攻击通过使用 HTTP GET 或 HTTP POST 请求执行 DDoS 攻击。这些请求是有效的&#xff0c;并且针对可用资源&#xff0c;因此很难防范 HTTP 洪水攻击。 匿名…

特斯拉——使用人工智能制造智能汽车

特斯拉(Tesla)是电动汽车开发和推广的先驱。特斯拉对自动驾驶汽车的未来寄予厚望--实际上&#xff0c;每一辆特斯拉汽车都有可能通过软件升级成为自动驾驶汽车。该公司还生产和销售高级电池和太阳能电池板。 汽车的自动驾驶是按从1~5的等级划分的。自适应巡航控制和自动停车系…

论文笔记(整理):轨迹相似度顶会论文中使用的数据集

0 汇总 数据类型数据名称数据处理出租车数据波尔图 原始数据&#xff1a;2013年7月到2014年6月&#xff0c;170万条数据 ICDE 2023 Contrastive Trajectory Similarity Learning with Dual-Feature Attention 过滤位于城市&#xff08;或国家&#xff09;区域之外的轨迹 过…

【SSL】用Certbot生成免费HTTPS证书

1. 实验背景 服务器&#xff1a;CentOS7.x 示例域名&#xff1a; www.example.com 域名对应的web站点目录&#xff1a; /usr/local/openresty/nginx/html 2. 安装docker # yum -y install yum-utils# yum-config-manager --add-repo https://download.docker.com/linux/ce…

采集SEO方法-优化内链与外链建设

采集大量的文章数据&#xff0c;要想批量做SEO优化添加内链外链方法&#xff0c;可以使用简数采集器的处理规则实现。 简数采集器的一个处理规则&#xff0c;可以包含多种SEO方法&#xff0c;还可自由组合&#xff0c;强大灵活方便。 优化内链外链的SEO技巧&#xff1a; 1&a…

基于PYQT5的GUI开发系列教程【二】QT五个布局的介绍与运用

目录 本文概述 作者介绍 创建主窗口 水平布局 垂直布局 栅格布局 分裂器水平布局 分裂器垂直布局 自由布局 取消原先控件的布局的方法 尾言 本文概述 PYQT5是一个基于python的可视化GUI开发框架&#xff0c;具有容易上手&#xff0c;界面美观&#xff0c;多平台…

基于SpringBoot的教学资源库系统的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 社会的进步&#xff0c;教育行业发展迅速&#xff0c;人们对教育越来越重视&#xff0c;在当今网络普及的情况下&#xff0c;教学模式也开始逐渐网络化&#xff0c;各大…

Python大数据之Python进阶(四)进程的注意点

文章目录 进程的注意点1. 进程的注意点介绍2. 进程之间不共享全局变量3. 进程之间不共享全局变量的小结4. 主进程会等待所有的子进程执行结束再结束5. 主进程会等待所有的子进程执行结束再结束的小结 进程的注意点 学习目标 能够说出进程的注意点 1. 进程的注意点介绍 进程之…

Polygon Miden交易模型:Actor模式 + ZKP => 并行 + 隐私

1. 引言 前序博客&#xff1a; Polygon Miden&#xff1a;扩展以太坊功能集的ZK-optimized rollupPolygon Miden zkRollup中的UTXO账户混合状态模型 Polygon Miden为&#xff1a; ZK-optimized rollup由客户端生成证明完善Polygon ZK系列解决方案&#xff0c;致力于成为网络…

nodejs+vue网上婚纱购物系统elementui

便了用户足不出门也能进行购物的理念&#xff0c;方便了婚纱影楼的对商品的进一步管理,互联网成为人们快速获取、发布、和传递信息的重要渠道&#xff0c;它在人们政治、经济、生活等各个方面发挥着重要的作用。未来的时代是网络信息的时代&#xff0c;“网上生活方式”是人类今…

软件测试面试复盘

作者&#xff1a;爱塔居 专栏&#xff1a;测试 1、计算机网络七层协议&#xff1a;物理层、数据链路层、网络层、传输层、表示层、会话层、应用层&#xff08;面试问过这个&#xff09; 2.TCP/IP四层模型&#xff1a;应用层、传输层、网络层、网络接口层&#xff08;笔试问过&…

【CMU15-445 Part-14】Query Planning Optimization I

Part14-Query Planning & Optimization I SQL is Declarative&#xff0c;只告诉想要什么而不需要说怎么做。 IBM System R是第一个实现query optimizer查询优化器的系统 Heuristics / Rules 条件触发 静态规则&#xff0c;重写query来remove 低效或者愚蠢的东西&#xf…

基于springboot的洗衣店订单管理系统

目录 前言 一、技术栈 二、系统功能介绍 顾客信息管理 店家信息管理 店铺信息管理 洗衣信息管理 预约功能 洗衣信息 交流区 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#x…

Springboot中使用拦截器、过滤器、监听器

一、Servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09;、Interceptor&#xff08;拦截器&#xff09; Javaweb三大组件&#xff1a;servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09; Spring…

7.2 怎样定义函数

7.2.1 为什么要定义函数 主要内容&#xff1a; 为什么要定义函数 C语言要求所有在程序中用到的函数必须“先定义&#xff0c;后使用”。这是因为在调用一个函数之前&#xff0c;编译系统需要知道这个函数的名字、返回值类型、功能以及参数的个数与类型。如果没有事先定义&…

400G DR4 QSFP-DD光模块:数据中心应用全攻略

在当今数字化时代&#xff0c;对于企业和供应商来说&#xff0c;高速数据传输至关重要。随着对更快数据传输的需求不断攀升&#xff0c;400G DR4 QSFP-DD光模块已经成为高速网络的最新解决方案。本文将全面介绍400G DR4 QSFP-DD光模块在数据中心应用中的优势和技术规范。 什么…

基于Java的校园体育赛事竞赛管理系统设计与实现(源码+lw+部署文档+讲解等)

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

性能分析-java虚拟机性能监控

虚拟机性能监控 给一个系统定位问题的时候&#xff0c;知识、经验是关键基础&#xff0c;数据是依据&#xff0c;工具是运用知识处理数据的手段。这里说的数据包括&#xff1a;运行日志、异常堆栈、GC日志、线程快照&#xff08;threaddump/javacore文件&#xff09;、堆转储快…

2023-9-29 JZ32 从上往下打印二叉树

题目链接&#xff1a;从上往下打印二叉树 import java.util.*; import java.util.ArrayList; /** public class TreeNode {int val 0;TreeNode left null;TreeNode right null;public TreeNode(int val) {this.val val;}} */ public class Solution {public ArrayList<I…

redis-设置从节点

节点结构 节点配置文件 主节点 不变 6380节点 port 6380 slaveof 127.0.0.1 63796381节点 port 6381 slaveof 127.0.0.1 6380启动 指定配置文件的方式启动 D:\jiqun\redis\Redis-6380>redis-server.exe redis.windows.conf启动时&#xff0c;会触发同步数据命令 主节点…