莫队入门总结

这是一篇适合蒟蒻的讲解**** 大佬可以自行离开****

莫队是一种是离线的算法 , 即它在询问的时候是不会修改的, 所以我们可以通过调整询问的次序来获得答案。

比如区间3到5和区间3到6 ,他们之间只差了1 , 于是我们只需要看下新加进来的这个元素对原来答案的影响就好了 , 对吧?

问题是: 我们应该如何给询问区间排序使得时间复杂度最小呢???

这里直接给出答案(主要是我太弱了 , 不会讲 , 可以先自行思考 , 熟练了之后去看其他大佬的博客):
首先将整个区间分块 , 将左端点按块来排序 , 右端点按实际位置排序 ;
有点抽象 , 上代码看看 :

struct question{
	int l , r , id ; 
	bool operator < (const question &a ) const { return pos[l] == pos[a.l] ? r < a.r : pos[l] < pos[a.l] ;}
}q[maxn];

其中pos存的是这个点所对应的块的编号 ;

然后。。莫队就结束了。。。

先上个例题看看:对着题讲会好些
在这里插入图片描述
洛谷p2709

根据我们刚才的思路 ,先把询问排序 , 然后再移动区间即可 ;

手动模拟一下就可以懂了 ;
看图 : 在这里插入图片描述
其中cnt是我目前各个数出现的次数 ;
简简单单 ;
上代码:

#include<bits/stdc++.h>
using namespace std ;
#define maxn 50050
#define int long long
#define re register int
#define kkk signed main
#define mem(x) memset(x,0,sizeof(x))
inline int read(){
	int ans = 0 , f = 1 ; char ch = getchar() ;
	while(ch < '0'|| ch > '9' ) { if(ch == '-') f = -1 ; ch= getchar() ; }
	while(ch >= '0' && ch <= '9' ) ans = (ans << 3) + (ans << 1) + ch - '0' , ch = getchar() ;
	return ans ;
}
int  a[maxn] , pos[maxn] , L[maxn] , R[maxn] , cnt[maxn] , ans[maxn] , aans; 
int n , m , k , S , len ; 
struct question{
	int l , r , id ; 
	bool operator < (const question &a ) const { return pos[l] == pos[a.l] ? r < a.r : pos[l] < pos[a.l] ;}
}q[maxn];
inline void add(int x){
	aans-=cnt[a[x]]*cnt[a[x]];
    cnt[a[x]]++;
    aans+=cnt[a[x]]*cnt[a[x]];
}
inline void del(int x){
	aans-=cnt[a[x]]*cnt[a[x]];
    cnt[a[x]]--;
    aans+=cnt[a[x]]*cnt[a[x]];
}
kkk(){
//	freopen("1.in" , "r" , stdin) ; 
//	freopen("1.out" , "w" , stdout) ; 
	n = read() , m = read() ; k = read() ;
	for(int i  =1  ;i <= n / sqrt(n) + 1 ; i++)
	L[i] = 999999 ;
	S = sqrt(n) ; 
//	printf(" s : %lld\n" , S) ; 
	for(int i = 1 ; i <= n ; i++){
		a[i] = read() ; 
		pos[i] =  (i - 1)/ S + 1 ; 
		L[pos[i]] = min(L[pos[i]] , i) ; 
		R[pos[i]] = max(R[pos[i]] , i) ; 
	}
	for(int i = 1 ; i <= m ; i++){
		q[i].l = read() , q[i].r = read() ; 
		q[i].id = i  ; 
	}
	sort(q + 1 , q + 1 + m) ; 
	int l = 1 , r = 0 ; 
	for(int i = 1 ; i <= m ; i++){
		re ql = q[i].l , qr = q[i].r ; 
//		printf("query l : %lld r : %lld\n" , q[i].l , q[i].r ) ; 
		while(r < qr) add(++r) ; 
		while(r > qr) del(r--) ; 
		while(l < ql) del(l++) ; 
		while(l > ql) add(--l) ; 
		ans[q[i].id ] = aans ;  
	}
	for(int i = 1 ; i <= m ; i++){
		printf("%lld\n" , ans[i]) ; 
	}
	return 0 ;
}

其中L【i】 是第i块的左端点 , pos是这点属于第几块 , ans是存这个询问的答案 ;

然后完结撒花✿✿ヽ(°▽°)ノ✿ ;

我太弱了可能没讲明白 自己多手摸几次就好了)

莫队最基础就讲完了 , 其实还有很多玄学优化 (要不然你会发现很多tle
你这可以在搞定这篇之后再去看其他人的(其实我不懂
再次强调:本篇博客只面向像我一样的蒟蒻 , 如有不足 , 请各位神犇指出QWQ ;

发布了2 篇原创文章 · 获赞 4 · 访问量 65

猜你喜欢

转载自blog.csdn.net/qq_35044088/article/details/104288679