莫队算法

mind:

分块

离散化

通过合理地对询问排序,然后以较优的顺序暴力回答每个询问(两个指针左右扩张或缩小范围)

借题理解

Luogu P3901 数列找不同

莫队发展里程

1.首先这个题很明显可以暴力跑,然后开个桶记录一下(慢到家

2.珂以在区间([1,5])的基础上,去掉位置 (1)(即将左端点右移一位),加上位置 (6)(即将右端点右移一位),得到区间([2,6])的答案
对于([99999,100000])这样的鬼畜数据肯定会被卡

所以就莫队就从此把分块纳了进去,先对数据进行了排序,然后在由上述操作锁定区间

怎样排序捏??

莫队提供了这样一个排序方案:将原序列以(n^{1/2})为一块进行分块(分块的大小也珂以调整),排序第一关键字是询问的左端点所在块的编号,第二关键字是询问的右端点本身的位置,都是升序。然后我们用上面提到的“移动当前区间左右端点”的方法,按顺序求每个询问区间的答案,移动每一个询问区间左右端点可以求出下一个区间的答案

排序:

bool cmp(Query a,Query b)
{ 
    return a.bl==b.bl?a.r<b.r:a.bl<b.bl;
}

但由于出题人过于狠毒
又多出一种优化,叫做奇偶优化
按奇偶块排序。这也是比较通用的。如果区间左端点所在块不同,那么就直接按左端点从小到大排;如果相同,奇块按右端点从小到大排,偶块按右端点从大到小排

node

 bool cmp(Query a,Query b)
{
    return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);
}

核心代码

 sort(q + 1,q + m + 1, cmp);
   for(int i = 1;i <= m; i++){
   	  L = q[i].l,R = q[i].r;
   	  while(nl < L) del(nl++);
	  while(nl > L) add(--nl);
	  while(nr < R) add(++nr);
	  while(nr > R) del(nr--);
	  if(ans == (R - L + 1)){
	  	  ANS[q[i].num] = 1;
	  }  
   }

复杂度

(O(n*n^{1/2}))

/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
typedef long long ll;
int read(){
	int x = 0,f = 1;char c = getchar();
	while(c < '0'||c > '9'){
	if(c == '-')f = -1;c = getchar();}
	while(c >= '0'&& c <= '9'){x = x*10 + c - '0',c = getchar();}
	return x*f;
}
int n,m,lucky_block,L,R,a[N],nl,nr,ANS[N],ans,cnt[N]; 
struct Query{
	int l, r , num;
}q[N];
bool cmp(Query a,Query b){
    return (a.l/lucky_block) == (b.l/lucky_block) ? a.r < b.r : a.l < b.l;//排序 
}
void add(int pos){
	if(++cnt[a[pos]] == 1) ans++;
}
void del(int pos){
	if(--cnt[a[pos]] == 0) ans--;
}
int main(){
   n = read(),m = read();
   lucky_block = sqrt(n);
   for(int i = 1;i <= n; i++){
   	    a[i] = read();
   }
   for(int i = 1; i <= m; i++){
   	   q[i].l = read();
   	   q[i].r = read();
   	   q[i].num = i;
   } 
   sort(q + 1,q + m + 1, cmp);
   
   for(int i = 1;i <= m; i++){
   	  L = q[i].l,R = q[i].r;
   	  while(nl < L) del(nl++);
	  while(nl > L) add(--nl);
	  while(nr < R) add(++nr);
	  while(nr > R) del(nr--);
	  if(ans == (R - L + 1)){
	  	  ANS[q[i].num] = 1;
	  }  
   }
   for(int i = 1;i <= m; i++){
   	   if(ANS[i] == 1){
   	     printf("Yes
");	
	   }
	   else{
	   	  printf("No
");
	   }
   } 
}

原文地址:https://www.cnblogs.com/Arielzz/p/14127113.html