【算法】莫队

这篇具有很好参考价值的文章主要介绍了【算法】莫队。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这篇博客起源于本人把一道 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文章来源地址https://www.toymoban.com/news/detail-730200.html

到了这里,关于【算法】莫队的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 007+limou+C语言基础排序算法(上)

    您好这里是limou3434的一篇博文,感兴趣可以看看我的其他内容。 排序算法简单理解就是:一串数组经过排序算法后得到有序的数组。排序算法在不同应用场景有不同的效果,因此我们有必要了解一些基础的排序算法。 而本次我给您带来的是一些基础的排序算法,主要涉及四

    2024年02月11日
    浏览(47)
  • 顺序表基本操作算法——基础代码(C语言)

     创建一个顺序表(数据元素个数为5), 输出顺序表中的所有数据元素 查找第3个位置上的元素 查找元素15是否在顺序表中,如果在,请输出该元素在顺序表中的位置 在顺序表中的第1个位置插入数据0 删除刚刚插入的元素 输出顺序表中的所有数据元素 运行结果如下  

    2024年02月06日
    浏览(49)
  • kotlin基础--快速上手kotlin语言开发

    1.1 变量 var表示可变变量,val表示不可变变量,注意并不是常量。变量名写在前面,类型写在后面,编译器如果能推断出你的类型,那么类型是不用声明的 。 编译器自动推断类型。 空安全类型编译器报错 如果还是想给赋初始化值的话 注意:String和String?是两个完全不同的类

    2024年02月15日
    浏览(54)
  • 【HarmonyOS北向开发】-04 ArkTS开发语言-ArkTS基础知识

     飞书原文档:Docs

    2024年02月11日
    浏览(55)
  • 自动化理论基础(2)—开发语言之Python

    一、知识汇总 掌握 Python 编程语言需要具备一定的基础知识和技能,特别是对于从事自动化测试等领域的工程师。以下是掌握 Python 的一些关键方面: 基本语法: 理解 Python 的基本语法,包括变量、数据类型、运算符、条件语句、循环语句等。 数据结构: 熟悉并能够使用

    2024年01月18日
    浏览(61)
  • 算法 时间、空间复杂度的计算(C语言/小白/零基础/新手 + 例题)

    目录 1. 时间复杂度 计算时间复杂度( O(N))的方法:   例1:嵌套循环时间复杂度的计算      例2:双重循环时间复杂度的计算   例3:常熟循环的时间复杂度   例6:冒泡排序的时间复杂度   例7: 二分查找的时间复杂度   例8:斐波那契的时间复杂度         常见的时间

    2024年02月08日
    浏览(42)
  • 【Go】Go语言开发0基础7天入门 - 笔记

    课程来源:【路飞学城】-黑金年卡VIP课程 课程名称:GO语言开发0基础7天入门 讲师:【 前汽车之家架构师 】Wusir-银角大王 官网:点击进入 集python简洁 + C语言性能 详情点击 编程语言 实战经验 源码 并发架构 新语言触类旁通 1.1 开篇介绍(必看) 1.2 环境搭建前戏 1.3 mac系统G

    2024年02月16日
    浏览(50)
  • go语言从0基础到安全项目开发实战

    搭建环境比较简单 到以下链接下 Go下载 - Go语言中文网 - Golang中文社区 下载windows版本64位zip包 https://studygolang.com/dl/golang/go1.20.7.windows-amd64.zip 不配置的话就只能在bin目录下才能运行go命令 创建test.go文件 然后代码如下 编译运行  两种方式编译运行代码 1.先 go build test.go编译成

    2024年02月13日
    浏览(47)
  • Go语言 -- Web开发基础学习 net/http包

    Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。 Go语言最擅长的领域就是Web开发,此贴是本人入门完go语法基础后学习Web开发的学习笔记。 新建go文件hello_world.go 写入: 在命令行运行: go run ./hello_world.go 可以发现控制台输出以下信息 通过上述代码

    2024年02月06日
    浏览(52)
  • 【鸿蒙开发】第七章 ArkTS语言UI范式-基础语法

    通过前面的章节,我们基本清楚鸿蒙应用开发用到的语言和项目基本结构,在【鸿蒙开发】第四章 Stage应用模型及项目结构也提到过ArkTS的UI范式的 基本语法 、 状态管理 、 渲染控制 等能力,简要介绍如下: 基本语法 : ArkTS 定义了 声明式UI描述 、 自定义组件 和 动态扩展

    2024年02月03日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包