莫队算法

引入

如果你已知一个序列,你已知其 \([2,6]\) 的区间和以及原序列,现在要你求\([2,7]\)的区间和,你会怎么办?

显然我们可以在 \([2,6]\) 的基础上加上原序列的第 7 位,就得到了 \([2,7]\) 的区间和。

同理,如果我们需要求出 \([2,5]\) 的区间和,我们只需要在 \([2,6]\) 的基础上减去原序列的第 5 位即可。

很好理解对吧,莫队的思想就是这样,每次这样一位一位的扩展。

优化

莫队可以用于解决一些区间问题。由于含有多次询问,莫队得以展现其优势。

但是对于一种类型的情况:

查询区间\([1,1] [n,n] [1,1] [n,m]\)...

此时我们每次询问直接处理了整个序列,复杂度直接起飞\(O(nm)\)

所以我们对询问进行离线,并对询问的左端点分块,然后按照左端点所在块为第一关键字,右端点为第二关键字的规则进行排序,复杂度大幅降低。

格式:

bool cmp(ques x,ques y){
	return x.pos==y.pos?x.r<y.r:x.pos<y.pos;
}

格式:

	int siz=sqrt(n);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;q[i].pos=q[i].l/siz;
	}
	sort(q+1,q+1+m,cmp);
	l=1;r=0;
	for(int i=1;i<=m;++i){
		int L=q[i].l,R=q[i].r,K=q[i].id;
		while(l<L) Sub(l++);
		while(R<r) Sub(r--);
		while(L<l) Add(--l);
		while(r<R) Add(++r);
		ans[K]=res; 
	}
	for(int i=1;i<=m;++i){
		printf("%lld\n",ans[i]);
	}

一道例题P1494 国家集训队小Z的袜子

我们只需要考虑 Add 和 Sub 函数怎么写就好啦

这个题很简单,注意细节就好啦

#include<bits/stdc++.h>
using namespace std;
#define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
#define LL long long
#define N 50010
int n,m;
int col[N];
LL uans[N],dans[N];
struct ques{
	int l,r,id;
	int pos;
}q[N];
int l,r;
int tim[N];
LL ups,downs;
void Add(int p){
	//printf("	+%d %d %d\n",p,col[p],tim[col[p]]);
	ups+=2*tim[col[p]];
	++tim[col[p]];
}
void Sub(int p){
	//printf("	-%d %d %d\n",p,col[p],tim[col[p]]);
	--tim[col[p]];
	ups-=2*tim[col[p]];
}
LL gcd(LL x,LL y){
	if(y==0) return x;
	return gcd(y,x%y);
}
bool cmp(ques x,ques y){
	return x.pos==y.pos?x.r<y.r:x.pos<y.pos;
}
int main(){
	//file(a);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&col[i]);
	}
	int siz=sqrt(n);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;q[i].pos=q[i].l/siz;
	}
	sort(q+1,q+1+m,cmp);
	l=1;r=0;
	for(int i=1;i<=m;++i){
		//printf("%d %d:\n",q[i].l,q[i].r);
		int L=q[i].l,R=q[i].r,K=q[i].id;
		if(L==R){
			uans[K]=0,dans[K]=1; 
			continue;
		}
		downs=1ll*(R-L+1)*(R-L);
		while(l<L) Sub(l++);
		while(R<r) Sub(r--);
		while(L<l) Add(--l);
		while(r<R) Add(++r);
		uans[K]=ups;dans[K]=downs; 
	}
	for(int i=1;i<=m;++i){
		LL Gcd=gcd(uans[i],dans[i]);
		printf("%lld/%lld\n",uans[i]/Gcd,dans[i]/Gcd);
	}
	return 0;
}
posted @ 2022-02-24 11:47  cbdsopa  阅读(52)  评论(0编辑  收藏  举报