莫队算法学习笔记

莫队学习笔记

莫队算法是一种优雅的暴力,常用于询问多个区间信息

主要思想

考虑一个问题:要查询[l,r]区间的信息,那么显然和[l,r-1]和[l-1,r]区间有着很大一部分重合,那么我们就可以考虑从其他相近的区间通过两个指针L和R来转移

实现

把每次询问的区间给保存下来,离线来做,这样我们可以得到一些区间,我们对这些区间进行排序(具体排序方式后面会讲到),对于第一个区间我们进行暴力查询,对于后面的所有区间我们就可以通过两个指针左右移动来询问了

排序方式

  1. 很显然的一种排序方式,按x从小到大排序,x相等时按照y从小到大排序,这样当然是可以的。这里给出一种不太友好的数据[1,10000000]和[2,1],容易发现我们的R指针会进行大量的移动,与我们所期望的不符,那么就诞生了第二种排序方法
  2. 把序列分块,如果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;
}
posted @   爱艺诗篇  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示