莫队(基础版)优雅的暴力

 莫队算法是一种离线算法,常用于高效处理区间查询问题。它通过合理排序和移动左右端点来减少时间复杂度。

基本思想

莫队算法的核心思想是将所有查询离线排序!!(找出一个过起来最快的查询顺序),然后通过移动当前区间的左右端点来处理每个查询。排序的方式通常是按照分块(也称为 "块")进行,这样可以将时间复杂度优化到 O (n√n)。

比如,查询(1,99)(2,7)(1,97)(3,20)

如果直接暴力,,呵呵,如果直接利用左右端点移动更新,会反复横跳,但是如果改变查询顺序,

(2,7)(3,20)(1,97)(1,99)就不怎么跳了

ps:端点移动:就是比如你先暴力了2-7的数,并且记录了各个数有几次(cnt),并且保留2-7的种类数,那么接下来查询3-20,你就可以移动左端,变成3,此时把原本2的数给减了(cnt[?]--),如果这个减了后变成0,说明这个查询内已经没有这个数,就可以把种类数--了,同理,加也一样,如果是加上变成1,说明从无到有,种类++;;;;;对于每一个查询,维护完种类,就把当前种类记入对应的查询原始标号内,最后依次输出

算法步骤

  1. 分块:将数组分成若干块,每块大小约为√n。
  2. 排序查询:按照左端点所在块的编号为第一关键字,右端点为第二关键字进行排序。
  3. 移动端点:维护当前区间的左右端点,通过移动端点来处理每个查询,并统计答案。

 

 这一题就是最基础的莫队查询

     

    #include <bits/stdc++.h>
    using namespace std;

    const int maxn = 100010;

    int n, m, block_size;
    int a[maxn], cnt[maxn], ans[maxn];
    int current_ans = 0, curL = 0, curR = 0;

    struct Query {
        int l, r, idx;
        bool operator<(const Query& other) const {
            if (l / block_size != other.l / block_size)
                return l / block_size < other.l / block_size;
            return (l / block_size) % 2 == 0 ? r < other.r : r > other.r;
        }
    };//利用了奇偶块进行分块

    分块详解

    莫队算法的关键在于如何排序查询区间,使得总移动距离最小。普通分块策略的步骤是:

    分块:将整个数据序列分成大小为 B 的块(通常 B ≈ √n

    排序:首先按左端点所在块的编号排序。。如果左端点在同一块,则按右端点排序

    可见 ,分块是为了计算出端点的范围并打上标记,方便进行排序,进行优化计算 

    bool operator<(const Query& other) const {//重定义<if (l / B != other.l / B)  // 按块编号排序,B是预先估算的块大小(因为通常情况大小和数量就是对n开根号,所以怎么理解都行)return l / B < other.l / B;return r < other.r;        // 左在同一块内按右端点排序
    }

    普通分块在处理同一区块内的查询时,右端点可能需要反复来回移动,导致额外的时间开销。奇偶分块优化的关键在于:

    偶数块:右端点按升序排列

    奇数块:右端点按降序排列

    这样处理同一区块内的查询时,指针移动形成 "之" 字形路径,减少了来回跳转的开销。

     如:(还不如不看,自己脑子里过一遍最清晰)

    Q1: [2, 10], Q2: [2, 4], Q3: [2, 7] (左端点都在块0,偶数块)
    

    排序后为 Q2 → Q3 → Q1,右指针路径:4 → 7 → 10,仍需往返。

    但考虑跨块的情况:

    Q1: [2, 10](块0), Q2: [5, 8](块1), Q3: [5, 12](块1)
    

    排序后:

    偶数块(块 0):Q1 [2, 10]

    奇数块(块 1):Q3 [5, 12] → Q2 [5, 8]

    处理完 Q1 后,右指针在位置 10,接下来处理 Q3,右指针只需扩展到 12;处理 Q2 时收缩到 8。避免了从 10 收缩到 8 再扩展到 12 的往返操作。

     

    Query queries[maxn];


    inline int read() {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return x * f;
    }

    void add(int pos) {
        if (++cnt[a[pos]] == 1) current_ans++;//加
    }

    void del(int pos) {
        if (--cnt[a[pos]] == 0) current_ans--;//减
    }

    int main() {
        n = read(); m = read();
        block_size = sqrt(n);
        
        for (int i = 1; i <= n; i++) 
            a[i] = read();
        
        for (int i = 0; i < m; i++) {
            queries[i].l = read();
            queries[i].r = read();
            queries[i].idx = i;
        }
        
        sort(queries, queries + m);
        for (int i = 0; i < m; i++) {
            int L = queries[i].l, R = queries[i].r;
            
            while (curL > L) add(--curL);
            while (curR < R) add(++curR);
            while (curL < L) del(curL++);
            while (curR > R) del(curR--);//核心,一下一下移动
            
            ans[queries[i].idx] = (current_ans == (R - L + 1));

                    //通过和总量判断,看看里面是不是有重复元素
        }
        
        for (int i = 0; i < m; i++)
            puts(ans[i] ? "Yes" : "No");
        
        return 0;
    }

    进阶一点(真就一点)

    完全在上一题基础上改进

    仅注解处改变

    #include <bits/stdc++.h>
    using namespace std;

    const int maxn = 50010;

    int n, m, block_size,k;//多了个k。。。
    int a[maxn];
    long long current_ans = 0,ans[maxn];//因为可能很大,开了ll,这里ans们记录的就是乘方后的维护了
    int cnt[maxn], curL = 1, curR = 0;

    struct Query {
        int l, r, idx;
        bool operator<(const Query& other) const {
            if (l / block_size != other.l / block_size)
                return l / block_size < other.l / block_size;
            return (l / block_size) % 2 == 0 ? r < other.r : r > other.r;
        }
    };
    Query queries[maxn];
    inline int read() {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return x * f;
    }
    void add(int pos) {
        if(a[pos]<=k){
        current_ans-=cnt[a[pos]]*cnt[a[pos]];
        ++cnt[a[pos]];
        current_ans+=cnt[a[pos]]*cnt[a[pos]];}
    }
    void del(int pos) {
        if(a[pos]<=k){
        current_ans-=cnt[a[pos]]*cnt[a[pos]];
        --cnt[a[pos]];
        current_ans+=cnt[a[pos]]*cnt[a[pos]];}//加减法都变成了对乘方的维护,而且判断题目条件,减少计算开销
    }

    int main() {
        n = read(); m = read();k = read();//k
        block_size = sqrt(n);
        for (int i = 1; i <= n; i++)
            a[i] = read();
        
        for (int i = 0; i < m; i++) {
            queries[i].l = read();
            queries[i].r = read();
            queries[i].idx = i;
        }
        sort(queries, queries + m);
        for (int i = 0; i < m; i++) {
            int L = queries[i].l, R = queries[i].r;
            while (curL > L) add(--curL);
            while (curR < R) add(++curR);
            while (curL < L) del(curL++);
            while (curR > R) del(curR--);

            ans[queries[i].idx] = current_ans;//把实时计算的乘方和映射到原始序列的数组锂
        }
        
        for (int i = 0; i < m; i++)
            printf("%lld\n",ans[i]);
        return 0;
    }

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

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

    相关文章

    ✨ Python 高级定制 | 美化 Word 表格边框与样式(收货记录增强版)

    之前我们完成了 Excel 数据提取、Word 表格写入与合并&#xff0c;现在继续 为 Word 表格添加高级样式 装扮&#xff0c;包括单元格边框、背景填色、居中对齐、粗体、高亮行/列等&#xff0c;进一步增强表格的可读性与专业性。 &#x1f58c;️ 样式设置函数 1. 设置单元格边框…

    Clickhouse源码分析-TTL执行流程

    第一种情况&#xff1a;无ttl_only_drop_parts配置 总体示例以及说明 如果没有ttl_only_drop_parts的配置&#xff0c;过期数据的删除&#xff08;这里是删除&#xff0c;是将过期的数据从这个part删除&#xff0c;并将过期的数据构成一个part&#xff0c;这个过期的part标记…

    elementui修改radio字体的颜色和圆圈的样式

    改完 <div class"choose"><el-radio-group v-model"radioNum"><el-radio label"1" size"large">Option 1</el-radio><el-radio label"2" size"large">Option 2</el-radio>&l…

    力扣3381. 长度可被 K 整除的子数组的最大元素和

    由于数据范围是2*10^5所以必然是遍历一次&#xff0c;子数组必定要用到前缀和&#xff0c;之前的题目中总是遇到的是子数组的和能不能被k整除&#xff0c;而这里不一样的是子数组的长度能不能被k整除&#xff0c;如果单纯的枚举长度必定超时&#xff0c;而看看题解得出的思路&a…

    基于SSM的勤工助学系统的设计与实现

    第1章 摘要 基于SSM框架的勤工助学系统旨在为学生、用工部门和管理员提供高效便捷的管理平台。系统包括学生端、用工部门端和管理员端&#xff0c;涵盖了从岗位发布、申请审核、工时记录、薪资管理到数据统计等完整的功能需求。 学生可以通过系统首页浏览最新的岗位信息和公告&…

    2025年06月30日Github流行趋势

    项目名称&#xff1a;twenty 项目地址 URL&#xff1a;https://github.com/twentyhq/twenty项目语言&#xff1a;TypeScript历史 star 数&#xff1a;31,774今日 star 数&#xff1a;1,002项目维护者&#xff1a;charlesBochet, lucasbordeau, FelixMalfait, Weiko, bosiraphae…

    creo 2.0学习笔记

    Creo软件从入门到精通——杜书森 1.1 Creo基本建模过程介绍 新建-零件-改名称-取消使用默认模板&#xff0c;是因为默认的是英制尺寸&#xff0c;自定义可选择mmns_part_solid&#xff0c;模板主要是设置模型的单位拉伸-选取FRONT-点击草绘视图&#xff0c;可进行草绘旋转——…

    ZNS初步认识—GPT

    1. ZNS SSD 的基本概念 Zoned Namespace (ZNS): ZNS 是一种新的NVMe接口规范&#xff0c;它将SSD的逻辑块地址空间划分为多个独立的、固定大小的“区域”&#xff08;Zones&#xff09;。区域 (Zone): ZNS SSD 的基本管理单元。每个区域都有自己的写入指针&#xff08;write p…

    【seismic unix生成可执行文件-sh文件】

    Shell脚本文件&#xff08;.sh文件&#xff09;简介 Shell脚本文件&#xff08;通常以.sh为扩展名&#xff09;是一种包含Shell命令的文本文件&#xff0c;用于在Unix/Linux系统中自动化执行任务。它由Shell解释器&#xff08;如Bash、Zsh等&#xff09;逐行执行&#xff0c;常…

    Debezium日常分享系列之:在 Kubernetes 上部署 Debezium

    Debezium日常分享系列之&#xff1a;在 Kubernetes 上部署 Debezium 先决条件步骤部署数据源 (MySQL)登录 MySQL db将数据插入其中部署 Kafka部署 kafdrop部署 Debezium 连接器创建 Debezium 连接器 Debezium 可以无缝部署在 Kubernetes&#xff08;一个用于容器编排的开源平台…

    利润才是机器视觉企业的的“稳定器”,机器视觉企业的利润 = (规模经济 + 技术差异化 × 场景价值) - 竞争强度

    影响机器视觉企业盈利能力的关键因素。这个公式本质上反映了行业的核心动态:利润来自成本控制(规模化效应)和差异化优势(技术壁垒与场景稀缺性的协同),但被市场竞争(内卷程度)所侵蚀。下面我将一步步拆解这个公式,结合机器视觉行业的特点(如工业自动化、质检、安防、…

    EPLAN 中定制 自己的- A3 图框的详细指南(一)

    EPLAN 中定制 BIEM - A3 图框的详细指南 在智能电气设计领域&#xff0c;图框作为图纸的重要组成部分&#xff0c;其定制的规范性和准确性至关重要。本文将以北京经济管理职业学院人工智能学院的相关任务为例&#xff0c;详细介绍在 EPLAN 软件中定制 BIEM - A3 图框的全过程…

    macbook开发环境的配置记录

    前言&#xff1a;好多东西不记录就会忘记 git ssh配置 当我们的没有配置git ssh的时候&#xff0c;使用ssh下载的时候会显示报错“make sure you have the correct access rights and respository exits" 如何解决&#xff0c;我们先在命令行检查检查一下用户名和邮箱是…

    GitLab 18.1 高级 SAST 已支持 PHP,可升级体验!

    GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

    [学习]M-QAM的数学原理与调制解调原理详解(仿真示例)

    M-QAM的数学原理与调制解调原理详解 QAM&#xff08;正交幅度调制&#xff09;作为现代数字通信的核心技术&#xff0c;其数学原理和实现方法值得深入探讨。本文将分为数学原理、调制解调原理和实现要点三个部分进行系统阐述。 文章目录 M-QAM的数学原理与调制解调原理详解一、…

    图书管理系统练习项目源码-前后端分离-使用node.js来做后端开发

    前端学习了这么久了&#xff0c;node.js 也有了一定的了解&#xff0c;知道使用node也可以来开发后端&#xff0c;今天给大家分享 使用node 来做后端&#xff0c;vue来写前端&#xff0c;做一个简单的图书管理系统。我们在刚开始学习编程的时候&#xff0c;需要自己写大量的项目…

    【甲方安全视角】企业建设下的安全运营

    文章目录 一、安全运营的概念与起源二、安全运营的职责与定位三、安全运营工程师的核心能力要求四、安全运营的典型场景与应对技巧1. 明确责任划分,避免“医生做保姆”2. 推动机制:自下而上 vs. 自上而下3. 宣传与内部影响力建设五、安全运营的战略意义六、为何需要安全原因在…

    03认证原理自定义认证添加认证验证码

    目录 大纲 一、自定义资源权限规则 二、自定义登录界面 三、自定义登录成功处理 四、显示登录失败信息 五、自定义登录失败处理 六、注销登录 七、登录用户数据获取 1. SecurityContextHolder 2. SecurityContextHolderStrategy 3. 代码中获取认证之后用户数据 4. 多…

    IPLOOK 2025上半年足迹回顾:连接全球,步履不停

    2025年上半年&#xff0c;IPLOOK积极活跃于全球通信舞台&#xff0c;足迹横跨亚洲、欧洲、非洲与北美洲&#xff0c;我们围绕5G核心网、私有网络、云化架构等方向&#xff0c;向来自不同地区的客户与合作伙伴展示了领先的端到端解决方案&#xff0c;深入了解各地市场需求与技术…

    【Kafka】docker 中配置带 Kerberos 认证的 Kafka 环境(全过程)

    1. 准备 docker 下载镜像 docker pull centos/systemd&#xff0c;该镜像是基于 centos7 增加了 systemd 的功能&#xff0c;可以更方便启动后台服务 启动镜像 使用 systemd 功能需要权限&#xff0c;如果是模拟 gitlab services 就不要使用 systemd 的方式启动 如果不使用 s…