【算法学习】莫队

大佬的博客

开幕雷击!我既然都贴上了大佬的博客,那还要我有什么用,但是我要是不记录的话早晚会忘,那既然是给自己看的话就象征性地写一下吧,等以后熟练了也不需要这博客了。

莫队虽然用到了分块,但是并不对分块进行操作,而是对分块的性质进行运用。

普通莫队

P1972 [SDOI2009] HH的项链

非常模板的题,但是卡莫队,所以自己去讨论区找双倍经验吧。

莫队是怎么一回事呢,我们把所有的询问都离线地储存下来,然后用 \(l,r\) 指针在数列上移动,在移动的过程中处理答案,在指针移动到查询区间时就储存当前答案,而这个查询区间的选择就很重要了,我们讲每个端点都分到不同的块内,对所属的块进行左端点排序,这样复杂度就降到了 \(O(n\sqrt{n})\)。(我不想证了 其实是不会

优化

排序奇偶性优化,当查询区间左端点在同一块内,判断所在的块是奇数就按右端点从小到大排序,是偶数就按从大到小排序,这样的话相当于是一个来回,可以很好地节约时间。

image

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+19;
int n,m;
int a[N];
int of[N];
int len;
int ans[N];
int cnt[N];
int now=0;
struct ss{
int l,r,id;
}q[N];
bool cmp(ss g,ss h){//奇偶数排序
return (of[g.l]^of[h.l])?of[g.l]<of[h.l]:(of[g.l]&1)?g.r<h.r:g.r>h.r;
}
void add(int pos){
if(!cnt[a[pos]]++) ++now;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
len=sqrt(n);//分块
for(int i=1;i<=n;i++){
of[i]=(i-1)/len+1;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
int ql=q[i].l,pr=q[i].r;
//移动指针
while(l<ql) now-=!--cnt[a[l++]];
while(l>ql) now+=!cnt[a[--l]]++;
while(r<pr) now+=!cnt[a[++r]]++;
while(r>pr) now-=!--cnt[a[r--]];
ans[q[i].id]=now;
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<"\n";
}
return 0;
}

带修莫队

通常情况下因为莫队是离线处理所以通常是不支持修改的,但如果并不强制修改的话,只是简单的修改的话带修莫队是可以实现的。

P1903 [国家集训队] 数颜色 / 维护队列

在上一题的基础上增加了单点修改操作,我们增加一个时间戳,对于每次修改都增加时间,每次询问都也储存一个时间戳,我们在移动指针时也要把时间移到查询区间的时间戳上,对应的数列上的数也要更换,答案也要更新。

跟上面代码差别不大,这种暴力算法好处就是写着简单。

注意点细节,注意块长取 \(n^{\frac{2}{3}} (\sqrt[3]{n^2})\),什么?要证明?没有。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int n,m;
int a[N];
struct ss{
int l,r,t,id;
}q[N];
int qc=0;
int ct[N];
int cpos[N];
int c[N];
int cnt[N];
int ans[N];
int of[N];
int cc=0;
int len;
bool cmp(ss g,ss h){
return (of[g.l]^of[h.l])?of[g.l]<of[h.l]:(of[g.r]^of[h.r])?of[g.r]<of[h.r]:g.t<h.t;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
len=pow(n,2.0/3.0);
for(int i=1;i<=n;i++){
of[i]=(i-1)/len+1;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
char op;
int l,r;
cin>>op>>l>>r;
if(op=='Q'){
qc++;
q[qc].l=l;
q[qc].r=r;
q[qc].t=cc;
q[qc].id=qc;
}
else{
cc++;
cpos[cc]=l;
ct[cc]=r;
}
}
sort(q+1,q+1+qc,cmp);
int l=1,r=0,now=0,time=0;
for(int i=1;i<=qc;i++){
int ql=q[i].l,pr=q[i].r,qt=q[i].t;
while(l<ql) now-=!--cnt[a[l++]];
while(l>ql) now+=!cnt[a[--l]]++;
while(r>pr) now-=!--cnt[a[r--]];
while(r<pr) now+=!cnt[a[++r]]++;
while(time>qt){
if(ql<=cpos[time]&&cpos[time]<=pr){
now-=!--cnt[a[cpos[time]]]-!cnt[ct[time]]++;
}
swap(a[cpos[time]],ct[time]);
time--;
}
while(time<qt){
time++;
if(ql<=cpos[time]&&cpos[time]<=pr){
now-=!--cnt[a[cpos[time]]]-!cnt[ct[time]]++;
}
swap(a[cpos[time]],ct[time]);
}
ans[q[i].id]=now;
}
for(int i=1;i<=qc;i++){
cout<<ans[i]<<"\n";
}
return 0;
}

例题练习

普通莫队

P1494 [国家集训队] 小 Z 的袜子 莫队板子题,运用一些数学知识就可以解决。

posted @   sad_lin  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
点击右上角即可分享
微信分享提示