莫队
前置芝士
分块
算法理解
一种暴力顺序的优化
离线+暴力+分块
通常用于不带修只查询的一类问题
模板题P1972 [SDOI2009] HH的项链
首先使用暴力法求解此题
我们设我们有一个
莫队优化:将时间复杂度控制在
我们会发现影响时间复杂度的是什么呢?
我们将询问
每一次移动的时间复杂度就是相邻两个点的曼哈顿距离,可以发现当我们对于
所以我们可不可以限制一下振幅在一个区间内来控制复杂度
想到了分块,我们按照左端点所在块的编号为第一关键字,以右端点为第二关键字排序
考虑一下,左端点的振幅每次被限制在了
右端点当左端点在一个块内的顺序时是递增顺序的,也就是说在一个块内右端点移动的复杂度是
记住右端点不是按块来排序的,因为这样就是一个方格图,无法保证在一个方格中的点的顺序,导致路径增长
还有就是块长不一定是
莫队优化:
莫队常数很小,但同时一些卡莫队的题需要卡卡常才能过
有一种优化是奇偶性排序,在块的编号为奇数时我们按照右端点升序排序,偶数时降序,可以优化一点复杂度
总结:莫队就是在暴力的基础上离线优化了一下暴力的枚举顺序,使其控制在
代码也非常好写:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,t,block,ans;
int a[N],pos[N],cnt[N],num[N];
struct query{
int l,r,i;
}ask[N];
void allocate(){
block=(int)sqrt(n);
t=n/block;
if(n%block) t++;
for(int i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
}
}
bool cmp(query x,query y){
if(pos[x.l]==pos[y.l]){
if(pos[x.l]&1) return x.r<y.r;
else return x.r>y.r;
}
return pos[x.l]<pos[y.l];
}
void add(int x){
if(!cnt[a[x]]) ans++;
cnt[a[x]]++;
}
void del(int x){
cnt[a[x]]--;
if(!cnt[a[x]]) ans--;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
allocate();
scanf("%d",&m);
for(int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
ask[i]={l,r,i};
}
sort(ask+1,ask+1+m,cmp);
int L=1,R=0;
for(int i=1;i<=m;i++){
int l=ask[i].l,r=ask[i].r;
while(L<l) del(L),L++;
while(l<L) L--,add(L);
while(R<r) R++,add(R);
while(r<R) del(R),R--;
num[ask[i].i]=ans;
}
for(int i=1;i<=m;i++){
printf("%d\n",num[i]);
}
}
ps:此题卡莫队,我没有卡过去
P1903 [国家集训队] 数颜色 / 维护队列
带修莫队
莫队本来不支持修改,但通过加一个维度的方式可以使其支持简单修改,我们设
然后以l所在的块为第一关键字,r所在的块为第二关键字,t为第三关键字排序
然后我们设块长为
总复杂度为
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=133340,M=1e6+5;
int n,m,tot,block,ans,gg;
int a[N],pos[N],cnt[M],num[N];
struct query{
int l,r;
}ch[N];
struct opera{
int l,r,t,i;
}ask[N];
void allocate(){
block=pow(n,0.666);
for(int i=1;i<=n;i++){
pos[i]=(i-1)/block+1;
}
}
bool cmp(opera x,opera y){
if(pos[x.l]==pos[y.l]){
if(pos[x.r]==pos[y.r]) return x.t<y.t;
return pos[x.r]<pos[y.r];
}
return pos[x.l]<pos[y.l];
}
void add(int x){
if(!cnt[a[x]]) ans++;
cnt[a[x]]++;
}
void del(int x){
cnt[a[x]]--;
if(!cnt[a[x]]) ans--;
}
void change(int l,int r,int t){
if(ch[t].l<=r&&l<=ch[t].l){
del(ch[t].l);
}
swap(a[ch[t].l],ch[t].r);
if(ch[t].l<=r&&l<=ch[t].l){
add(ch[t].l);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
allocate();
for(int i=1;i<=m;i++){
int l,r;
char s[1];
scanf("%s%d%d",s,&l,&r);
if(s[0]=='Q'){
tot++;
ask[tot]={l,r,gg,tot};
}
else{
gg++;
ch[gg]={l,r};
}
}
sort(ask+1,ask+1+tot,cmp);
int L=1,R=0,T=0;
for(int i=1;i<=tot;i++){
int l=ask[i].l,r=ask[i].r,t=ask[i].t;
while(L<l) del(L),L++;
while(l<L) L--,add(L);
while(R<r) R++,add(R);
while(r<R) del(R),R--;
while(T<t) T++,change(L,R,T);
while(t<T) change(L,R,T),T--;
num[ask[i].i]=ans;
}
for(int i=1;i<=tot;i++){
printf("%d\n",num[i]);
}
}
P1494
切了,和莫队板子一样,我们会发现在一个区间里袜子配对总数为
所以我们就统计每一种颜色相同数目,然后和总数约一下分即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探