莫队学习笔记

莫队学习笔记

什么是莫队?

莫队是一种离线数据结构类的算法。
我们先考虑一类问题:
在一个序列上面查询区间信息。那么对于这样一类问题,首先想到各种高级的数据结构。
但莫队也可以很好的实现它。

普通莫队

莫队的思想是:如果我们已经了\([l,r]\)的区间信息,我们便可以通过指针转移暴力的快速转移到其他区间。这样,我们每一次都只需要通过前一次的区间信息得出下一次的区间。

但是有一个大问题,要是毒瘤出题人要你这个区间在最左边和最右边来回左右横跳,那就完了,时间复杂度被卡到了\(O(n^2)\)
怎么办?
莫队的精髓就在于离线操作,把所有的询问按分块的思想来调整,排序。我们可以把它们分成\(\sqrt{n}\)块来处理排序,这样子我们就可以减少区间跳动次数,来保证时间效率。
所以对于两次询问之间,我们先判定它们是不是属于同一个分块,如果是就以\(l\)作为关键字排序,否则以\(r\)为关键字排序
cmp代码:

bool cmp(int i, int j){
	if(l[i]/k != l[j]/k)	return l[i]<l[j];
	return r[i] < r[j];
}

来看一个例题:

洛谷P1494 [国家集训队]小Z的袜子

https://www.luogu.com.cn/problem/P1494
这道题其实就是让你求求一个区间内每种颜色数目的平方和。
code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int A = 50005;
int l[A], r[A], id[A], a[A];
int n, m, k;
int now;//区间和 
int ans[A];
int cnt[A];

bool cmp(int i, int j){
	if(l[i]/k != l[j]/k)	return l[i]<l[j];
	return r[i] < r[j];
}

void add(int x){
	now -= cnt[x] * (cnt[x] - 1) / 2; 
	++cnt[x];
	now += cnt[x] * (cnt[x] - 1) / 2;
}

void del(int x){
	now -= cnt[x] * (cnt[x] - 1) / 2; 
	--cnt[x];
	now += cnt[x] * (cnt[x] - 1) / 2;
} 

signed main(){
	cin>>n>>m;
	for(int i = 1; i <= n; i++)	cin>>a[i];
	for(int i = 1; i <= m; i++){
		cin>>l[i]>>r[i];
		id[i] = i;
	}
	k = sqrt(n);
	sort(id+1, id+m+1, cmp);
	int s = 1, t = 0;//当前区间 
	for(int j = 1; j <= m; j++){
		int i = id[j];
		while(s > l[i])	add(a[--s]);
		while(t < r[i]) add(a[++t]);
		while(s < l[i]) del(a[s++]);
		while(t > r[i]) del(a[t--]);
		ans[i] = now;
	}
	for(int i = 1; i <= m; i++){
		int tot = (r[i] - l[i] + 1) * (r[i] - l[i]) / 2;
		if(r[i] == l[i] or tot == 0){
			puts("0/1");
			continue;
		} 
		int g = __gcd(ans[i], tot);
		int aa = ans[i]/g, bb = tot/g;
		cout<<ans[i]/g<<'/'<<tot/g<<endl;
	}
	return 0;
} 
posted @ 2021-12-04 19:10  WRuperD  阅读(35)  评论(0)    收藏  举报

本文作者:DIVMonster

本文链接:https://www.cnblogs.com/guangzan/p/12886111.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

这是一条自定义内容

这是一条自定义内容