莫队算法学习笔记
莫队学习笔记
莫队算法是一种优雅的暴力,常用于询问多个区间信息
主要思想
考虑一个问题:要查询[l,r]区间的信息,那么显然和[l,r-1]和[l-1,r]区间有着很大一部分重合,那么我们就可以考虑从其他相近的区间通过两个指针L和R来转移
实现
把每次询问的区间给保存下来,离线来做,这样我们可以得到一些区间,我们对这些区间进行排序(具体排序方式后面会讲到),对于第一个区间我们进行暴力查询,对于后面的所有区间我们就可以通过两个指针左右移动来询问了
排序方式
- 很显然的一种排序方式,按x从小到大排序,x相等时按照y从小到大排序,这样当然是可以的。这里给出一种不太友好的数据[1,10000000]和[2,1],容易发现我们的R指针会进行大量的移动,与我们所期望的不符,那么就诞生了第二种排序方法
- 把序列分块,如果x在同一个块内,那么按照右端点从大到小排序,这样可以解决上面的一组
bool cmp(query a,query b){ return (a.l/bl)==(b.l/bl)?a.r>b.r:a.l<b.l; }
此时就会涉及到一个问题:分块的块长一般设为多少?如果n和m同阶,一般设计为根号n,(但是某些题目可能需要设计为
n^0.67)
例题及代码
小B的询问
莫队裸题:
#include<bits/stdc++.h>
using namespace std;
//#define int long long
typedef __int128 i128;
typedef long long ll;
const int N=2e5+10;
int n,m,len,tmp;
int a[N],cnt[N];
ll ans[N];
struct Q{
int id,l,r;
} q[N];
int get(int x) {
return x/len;
}
bool cmp(const Q& a,const Q& b) {
int i=get(a.l),j=get(b.l);
if(i!=j) return i<j;
return a.r>b.r;
}
void add(int x,ll& res){
res-=cnt[x]*cnt[x];
cnt[x]++;
res+=cnt[x]*cnt[x];
}
void del(int x,ll& res){
res-=cnt[x]*cnt[x];
cnt[x]--;
res+=cnt[x]*cnt[x];
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>tmp;
len=sqrt(n);
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1,l,r; i<=m; i++) {
cin>>l>>r;
q[i]=(Q){i,l,r};
}
sort(q+1,q+1+m,cmp);
int i=1,j=0;
ll res=0;
for(int k=1;k<=m; k++) {
int id=q[k].id,l=q[k].l,r=q[k].r;
while(i<l) del(a[i++],res);
while(i>l) add(a[--i],res);
while(j<r) add(a[++j],res);
while(j>r) del(a[j--],res);
ans[id]=res;
}
for(int i=1;i<=m;i++)
cout<<ans[i]<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)