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