关于莫队

article/2025/8/8 21:45:45

待补:树上莫队,二次离线莫队

关于莫队 其实是个神奇的暴力算法..

通常就是一种处理区间的东西 通过上一次的的左端点和右端点跳来跳去 从而得到这次区间的值

很暴力啊...完全不用思考就能懂的 那么 接下来首先是

普通莫队

排序和移动两点提及一下

关于排序 :

Q1 : 为什么要排序?

A1 : 因为可能两次询问的区间不重叠 如 [1,233] 和 [233,2333] 那多麻烦

Q2 : 那按左端点位置排还是右端点位置排?

A2 : 都是片面的 举个例子 [1,2333] [2,4] [3,2333] [4,6] 这几个如果按左端点排 右端点要跳四千多次 另一个同理

Q3 : 那要怎么排呢?

A3 : 分块排序 同一块中按其中一种排序 不同块中就按另一种排序 然后块的话....自己分个合适的就好 不要一个一块之类就好

然后听说这个的复杂度比较玄学......

关于处理每个询问 :

左右端点同理 这里以左为 Eg :

    如果 原来的 l 在 本次 l 的 左边 说明右侧部分区间无用但上次记录了 于是往右移 删除第 l 个点的影响 然后使 l = l + 1

    如果 原来的 l 在 本次 l 的 右边 说明左侧部分区间有用但上次没记录 于是往左移 增加第 l - 1 个点的影响 并使 l = l - 1

好了下放例题 然后下放代码

#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 100010;
struct bot {int l,r,id;
} s[MAXN];
int ans[MAXN],tot[MAXN],v[MAXN],base,l,r,answ;
inline short cmpto(bot x,bot y) {return x.l/base==y.l/base?x.r<y.r:x.l<y.l;}
inline void del(int p){if (!(--tot[v[p]])) --answ;}
inline void inc(int p){if (++tot[v[p]] == 1) ++answ;}
int main()
{int n,m;scanf("%d%d",&n,&m),base = sqrt(m);for (int a = 1 ; a <= n ; ++ a) scanf("%d",&v[a]);for (int a = 1 ; a <= m ; ++ a) scanf("%d%d",&s[a].l,&s[a].r),s[a].id = a;sort(s + 1,s + m + 1,cmpto);for (int a = 1 ; a <= m ; ++ a){int i = s[a].l,j = s[a].r;while (l < i) del(l++);while (l > i) inc(--l);while (r < j) inc(++r);while (r > j) del(r--);if (answ == j - l + 1) ++ans[s[a].id];}for (int a = 1 ; a <= m ; ++ a)if (ans[a]) printf("Yes\n");else printf("No\n");return 0;
}

然后来看看升级版的 题目戳这里 最后两个点卡莫队

然而事实上略略优化一下 然后 Take a O2 breath 就可以过了

主要还是分块大小需调整 这个优化对玄学算法很有帮助 不过很有碰运气的成分 例如 (下放AC链接)

https://www.luogu.org/record/show?rid=11544517

https://www.luogu.org/record/show?rid=11544631

这第二个的最后一个点是卡着 1000ms 过的......  然后感觉管理看见鄙人过了又要加强数据了 =-=

下放代码

#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 500005;
struct bot {int l,r,id;
} s[MAXN];
int v[MAXN],tot[MAXN << 1],ans[MAXN],l,r,base,answ;
inline short cmp(bot x,bot y) {return x.l/base==y.l/base?x.r<y.r:x.l<y.l;}
inline void del(int p) {if (!(--tot[v[p]])) --answ;}
inline void inc(int p) {if (++tot[v[p]] == 1) ++answ;}
inline int re()
{char q = getchar(); int x = 0;while (q < '0' || q > '9') q = getchar();while ('0' <= q && q <= '9') x = (x << 3) + (x << 1) + q - (3 << 4),q = getchar();return x;
}
int main()
{int n = re();base = n>=2333?2333:n;for (int a = 1 ; a <= n ; ++ a) v[a] = re();int m = re();for (int a = 1 ; a <= m ; ++ a) s[a].l = re(),s[a].r = re(),s[a].id = a;sort(s + 1,s + m + 1,cmp);for (int a = 1 ; a <= m ; ++ a){int i = s[a].l,j = s[a].r;while (l < i) answ -= !(--tot[v[l++]]);while (l > i) answ += ++tot[v[--l]] == 1;while (r < j) answ += ++tot[v[++r]] == 1;while (r > j) answ -= !(--tot[v[r--]]);ans[s[a].id] = answ;}for (int a = 1 ; a <= m ; ++ a) printf("%d\n",ans[a]);return 0;
}

好了话不多说 进入下一块 接下来是

带修莫队

看名字很明显 就是带修改的莫队

哇带修改好强啊 而且听说别称是可持久化莫队?难道还要开20倍空间存版本?

但别忘了我们的莫队是个暴力算法 你再持久也是暴力算 维护啥版本呢

于是 这里的时间就可以理解为 像左右一样的 一个维度就好

用处的话同普通莫队 就是查询区间 但是同时支持了修改区间 (或单点) 的值

没有想法?其实只添加了几行而已!不要以为查询版本很麻烦 其实也就像左右端点一样跳来跳去就好啦

关于排序

同普通莫队的排序 但同样我们可以举出 l 和 r 差不多 但是 版本 (用 e 表示) 每次要跳上千次的反例

所以我们排序还要考虑版本 即 l 和 r 都是 同一块的时候 按版本大小排 然后比较整洁的表示方法是这样的

short cmp(bot x,bot y)
{if (x.l / base != y.l / base) return x.l < y.l;//base是块大小if (x.r / base != y.r / base) return x.r < y.r;return x.edit < y.edit;
}

关于查询之前的版本预处理

我们需要记录第 sizq 次修改的点 和 修改前 修改后的值 再记录第 x 次询问时 修改了几个值就好

可以显然地发现 每次记录询问时 此时已经修改的次数肯定是递增 (可能同) 的 反正不可能减 然而这条并没有什么卵用.....

不过我们处理会方便一点!

修改的要像这样记录 其中 sizr 很显然是记录输入的修改操作的次数

	date[++sizr].ago = cpy[x];date[sizr].nod = x;date[sizr].ure = y;cpy[x] = y;

然后询问同普通莫队 不过要加上一个这个玩意 还有编号肯定不是 1 到 m 了 所以也要用一个数来存询问个数

	//s[a].id = a; 这句改成下一行的s[++sizq].id = sizq;s[sizq].edit = sizr;//上面就是存当前点修改的版本啦

正式关于查询

查询的话嘛 同普通莫队 但在这之前要把版本调到当前查询的版本

同样两个 while 一大于一小于 不多讲 (看代码绝对能懂了) 然后要记住 如果修改的点在区间里面 就要像左右端点加减一样 修改 answ 的值 但如果不在的话 修改 v 数组的值 就好了

哦对了关于 v 数组

就是读入初始值的数组啊 用这个顺便修改 因为左右端点跳的时候是通过 v 数组判定的 所以顺便要修改这个

然后之前预处理存修改的数组 因为要前驱所以也要用到 v 数组 所以建议开一个相同的数组 cpy 然后预处理时修改这个

还要还有 之前左右端点判重不是 ++--tot[v[p]] 嘛 因为查版本修改的数直接是颜色了 为了查询方便一点 改成 ++--tot[p]

其实完全可以在版本修改的子程序里重打一遍的...上面那种办法在外面的话 l++ r-- 之类的还要套上一个 v[ ]

继续关于查询

查询多出来的 while 长这样子

	z = s[a].edit;while (e < z) ++e,ate(date[e].nod,date[e].ure);while (e > z) ate(date[e].nod,date[e].ago),--e;

千万不要写成这样子

	z = s[a].edit;while (e < z) ate(date[++e].nod,date[e].ure);while (e > z) ate(date[e].nod,date[e--].ago);

然后 ate (就是 update 吃货别想歪了) 长这样

void ate(int p,int f)
{if (l <= p && p <= r) del(v[p]),inc(f);v[p] = f;
}

其他和普通莫队差不多了 下放例题 还有代码

#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 50005;
const int MAXM = 1000010;
struct bot {int l,r,edit,id;
} s[MAXN];
struct etion {int nod,ago,ure;
} date[MAXN];
int tot[MAXM],ans[MAXN],v[MAXN],cpy[MAXN],sizq,sizr,base,l,r,e,answ;
void inc(int p) {if (++tot[p] == 1) ++answ;}
void del(int p) {if (!(--tot[p])) --answ;}
void ate(int p,int f)
{if (l <= p && p <= r) del(v[p]),inc(f);v[p] = f;
}
short cmp(bot x,bot y)
{if (x.l / base != y.l / base) return x.l < y.l;if (x.r / base != y.r / base) return x.r < y.r;return x.edit < y.edit;
}
int main()
{char q[4];int n,m,x,y,z;scanf("%d%d",&n,&m);for (int a = 1 ; a <= n ; ++ a) scanf("%d",&v[a]),cpy[a] = v[a];for (int a = 1 ; a <= m ; ++ a){scanf("%s%d%d",q,&x,&y);if (q[0] == 'Q'){s[++sizq].id = sizq;s[sizq].edit = sizr;s[sizq].l = x;s[sizq].r = y;continue;}date[++sizr].ago = cpy[x];date[sizr].nod = x;date[sizr].ure = y;cpy[x] = y;}base = pow(n,2.0 / 3);//那种听说比base = sqrt(n);快诶sort(s + 1,s + sizq + 1,cmp);for (int a = 1 ; a <= sizq ; ++ a){x = s[a].l;y = s[a].r;z = s[a].edit;while (e < z) ate(date[++e].nod,date[e].ure);while (e > z) ate(date[e].nod,date[e--].ago);while (l < x) del(v[l++]);while (l > x) inc(v[--l]);while (r < y) inc(v[++r]);while (r > y) del(v[r--]);ans[s[a].id] = answ;}for (int a = 1 ; a <= sizq ; ++ a) printf("%d\n",ans[a]);return 0;
}

 


http://chatgpt.dhexx.cn/article/TVZPJeFm.shtml

相关文章

莫队(普通莫队,带修莫队,树上莫队)

听说 莫队算法是一种“优雅的暴力”&#xff08;小声bb&#xff09;。 普通莫队 1/ 引入 problem&#xff1a;给你一个长度为n的数组&#xff0c;有m次查询&#xff0c;每次查询询问一个区间[L, R]内有多少个不同的数。 首先想想暴力怎么做。 开一个数组用来记数&#xff0c…

回滚莫队解析

&#xff08;南昌理工ACM集训队&#xff09; 泻药&#xff0c;不匿&#xff08;bushi&#xff09; 浅谈莫队回滚莫队样例小结 浅谈莫队 莫队是要摸清回滚莫队必须掌握的前置技能(๑ŐдŐ)b 前两天打多校有幸接触了莫队算法&#xff0c;花了几天时间搞懂了这些东西&#xff0…

【莫队/树上莫队/回滚莫队】原理详解及例题:小B的询问(普通莫队),Count on a tree II(树上莫队),kangaroos(回滚莫队)

文章目录 问题引入介绍莫队算法及其实现过程时间复杂度莫队算法适用范围莫队奇偶优化普通莫队&#xff1a;小B的询问树上莫队&#xff1a;SP10707 COT2 - Count on a tree II回滚莫队&#xff1a;[PA2011]Kangaroos upd&#xff1a;2021-08-11&#xff1a;重新对博客进行了外观…

超全的莫队算法一遍过

前置知识&#xff1a;双指针、分块 简单概括而言&#xff0c;莫队是一个离线算法&#xff0c;核心思想是将所有的询问按照分块进行排序后&#xff0c;对于每个询问可以通过双指针增删数据来达到总体的复杂度。 莫队通常用来解决求一段区间内不同数的个数相关的问题。 目录 1、…

莫队(普通操作)C++

由于&R闲着无聊&#xff0c;今天上午学习的莫队也并不是完全掌握&#xff0c;老师才发的PPT也没有保存&#xff0c;于是决定乱写一篇靠着自己的理解写一篇关于莫队用法的博客......如果有错的麻烦之处&#xff0c;纯属&R自己理解有问题哈。 一.莫队的含义 莫队是在一…

【算法笔记】莫队算法(基础莫队,带修莫队,回滚莫队,树上莫队,二次离线莫队)

整理的算法模板合集&#xff1a; ACM模板 目录 1. 基础莫队2. 带修莫队3. 回滚莫队4. 树上莫队5. 二次离线莫队6. 在线莫队 来这里学习莫队以及神奇的证明&#xff1a;莫队算法 --算法竞赛专题解析&#xff08;26&#xff09; 我们首先考虑双指针的暴力法&#xff0c;发现很容…

linux(vscode)配置coderunner for python3

1.Python3 安装路径查看 which python3 2.vscode扩展中安装coderunner 3.配置coderunner 管理->设置->扩展->coderunner,找到下图所示内容&#xff0c;选择在settings.json中编辑。 在文件中输入code-runner.executorMap&#xff0c;会自动补全代码&#xff0c;如…

问题解决:VSCode Code Runner输出中文乱码

用vscode code runner插件经常会出现输出中文乱码的问题 那么如何解决这个问题呢&#xff1f; 解决方法&#xff1a; 通过设置&#xff0c;可以把代码放到 VS Code 内置的 Terminal 来运行&#xff0c;那么问题就能迎刃而解了。 选择 文件 -> 首选项 -> 设置&#xff0c…

VSCode——使用CodeRunner开发python输出中文出现乱码的解决方法

目录 方法一&#xff1a;每次都要加 方法二&#xff1a;一劳永逸 方法一 在程序中加入如下代码强行控制输出格式为utf-8即可 # -*- coding: utf-8 -*- import sys import io sys.stdoutio.TextIOWrapper(sys.stdout.buffer,encodingutf8)#改变默认输出的标准编码 编码改变…

修改CodeRunner快捷键

默认运行是 alt ctrl N, 改为alt ctrl r. run改更合理。 停止改为alt ctrl s stop也更合理&#x1f43b;

vscode用CodeRunner运行C++文件,提示系统找不到路径

问题描述 如图&#xff0c;直接点运行出现如下提示&#xff1a; 但是分开执行还是正常的&#xff1a; 原因 cmd执行多条语句要使用" && “作为连接符&#xff0c;对应的powerShell要用” ; "。 解决 在用户设置中修改code-runner.executorMap中对应的值…

vscode_Code Runner(codeRunner)配置(自定义输入映射内容)/指定.c/.cpp编译选型/编译运行含有中文名的文件示例(最佳实践)

文章目录 声明:文件(目录)中包含中文的问题关于配置调试功能(推荐) 找到配置入口点击在setting.json中编辑可以指定各种语言的命令行映射,这里以c语言指定以c99编译修改为例.可见到,届时编译.c文件的时候就会是以C99的标准指定编译. 运行效果 声明:文件(目录)中包含中文的问题 …

用VSCode 编写C++ 引入自定义头文件容易犯的错 和CodeRunner配置

问题描述 undefined reference to log(char const*) collect2.exe: error: ld returned 1 exit status 原因分析&#xff1a; 一般是由于c编译时&#xff0c;.cpp源文件未找到引起的 解决方案&#xff1a; 1.正确操作 一.例如你一个文件下有一个.h的文件和一个.cpp的文件 在v…

VSCode用Code Runner编译运行c/c++

首先vscode需要在文件夹下打开&#xff0c;以下所有的文件都需要放到你编写程序文件夹下的.vscode文件夹里 平时可以右击文件夹&#xff0c;选择open with code&#xff0c;结构应该是这样的 配置CodeRunner 下载 左下角齿轮找到设置 在扩展中找到Run Code configuration,点击…

【Mac】VScode配置Python开发环境详细教程(报错解决Import Error No module named ) CodeRunner插件

文章目录 在VScode中安装python插件解决报错SyntaxError: Non-ASCII character \xef in file解决报错"No module named xxx "VScode上使用Jupyter Notebook的方法20.02.26 最新方法&#xff1a;Code Runner插件一键安装&#xff08;python、java、C&#xff09;终端目…

CodeRunner插件:自定义编译参数

在设置里找到Coderunner选项&#xff0c;然后找到executorMap 点击 在 settings.json 中编辑 &#xff0c;你将看到对不同语言的编译指令。 以下为本人设置&#xff0c;为Mac系统&#xff0c;指令有所不同&#xff0c;不过操作流程是一样的。 "code-runner.executorMap…

java扩展包_CodeRunner 的 Java 扩展 Jar 包支持

CodeRunner 介绍 CodeRunner 是 Mac 上一款功能强大但使用简单代码工具&#xff0c;官方介绍支持几乎所有语言(20种语言)&#xff0c;同时支持语法高亮、代码提示和多种界面主题&#xff0c;在学习新的语言或编写简单测试代码时非常实用。 我常常用它来管理一些代码片段和测试不…

vscode 中 coderunner无法运行dart程序

如果你的vscode使用了CodeRunner&#xff0c;运行dart文件出错找不到dart的情况下: /bin/sh: dart: command not found可以直接修改coderunner扩展配置信息, 将其路径改为你希望的位置

vscode使用codeRunner将c/c++程序运行到外部控制台窗口

需要先关闭codeRunner的Run in Terminal 只需要在codeRunner的setting中&#xff0c;在从c/c的设置命令中添加 start cmd /c //或者/k /** 如果添加的是/c&#xff0c;则需要在程序的末尾添加system("pause"),不然会一闪而过&#xff0c; /k则不需要&#xff0c;不…

CodeRunner破解

CodeRunner2是Mac系统下的一款编程软件&#xff0c;支持23种语言和.txt文档制作&#xff0c;比Xcode都强大&#xff0c;Xcode只支持4种语言&#xff0c;原来的破解补丁有联网验证的问题&#xff0c;现在我做了程序防止联网验证。 破解方法&#xff1a;先下载CodeRunner2&#x…