莫队讲解--普通莫队

结束了分块,我们来讲下莫队。

据我所知,莫队能解决一切区间问题,除了翻转。因为它就是个暴力

其实这两者的关系并不大。仅仅是时间复杂度一样而已。

莫队只能解决离线问题,在线马上GG。

我们把原序列分成√n块(好像就是这里相同)。这里说的序列是查询序列L--R,并不是读入的a[i].

之后我们把序列排序:按照第一关键字为左端点所在的块的大小,如果相同就按照右端点大小排序。

总时间复杂度均摊之后是O(n√n)。

因为针对每√n次查询,左端点一共移动了√n次,而右端点一共移动了n次。

总时间复杂度再乘上√n,就是O(n√n+n)。完美!

有人会说:“莫队有何用啊,我线段树解决!”别急,看例题

BZOJ1878 [SDOI2009]HH的项链

求区间有多少种不同的数。

线段树成功GG,因为它并没有区间加和性。

考虑莫队加上一个桶,用delete和add函数解决,轻松AC

注:4个while是算法的核心,希望大家多加理解莫涛大神的算法。

#include<stdio.h>
#include<algorithm>
using namespace std;
#define mod 240
struct Node
{
    int x,y,blob,daan,id;
}query[200001];
int n,Q;
int a[50001];
int sum[1000100],ans;
bool cmp(const Node &a,const Node &b)
{
    if(a.blob!=b.blob)
        return a.blob<b.blob;
    return a.y<b.y;
}
bool cmp2(const Node &a,const Node &b)
{
    return a.id<b.id;
}
void delet(int l)
{
    sum[a[l]]--;
    if(sum[a[l]]==0)
        ans--;
}
void add(int l)
{
    sum[a[l]]++;
    if(sum[a[l]]==1)
        ans++;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    	scanf("%d",&a[i]);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
    	scanf("%d%d",&query[i].x,&query[i].y);
    	query[i].id=i;
    	if(query[i].x%mod!=0)
    		query[i].blob=query[i].x/mod+1;
    	else
    		query[i].blob=query[i].x/mod;
    }
    sort(query+1,query+Q+1,cmp);
    int l=0,r=0;
    for(int i=1;i<=Q;i++)
    {
    	while(l<query[i].x)
    		delet(l++);
    	while(l>query[i].x)
    		add(--l);
    	while(r<query[i].y)
    		add(++r);
     	while(r>query[i].y)
    		delet(r--);
    	query[i].daan=ans;
    }
    sort(query+1,query+Q+1,cmp2);
    for(int i=1;i<=Q;i++)
    	printf("%d\n",query[i].daan);
}

  

posted @ 2018-10-16 22:32  342  阅读(207)  评论(0编辑  收藏  举报