莫队讲解--普通莫队
结束了分块,我们来讲下莫队。
据我所知,莫队能解决一切区间问题,除了翻转。因为它就是个暴力
其实这两者的关系并不大。仅仅是时间复杂度一样而已。
莫队只能解决离线问题,在线马上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);
}

浙公网安备 33010602011771号