莫队算法,是莫涛dalao发明的一个神奇的优化暴力算法,它使用看似很simple的指针移动操做以及分块的思想来将复杂度优化至\(O(n\sqrt n)\)html
莫队的基本思想也很简单:git
不过若是是这样的话,只要出题人把数据造坑一点,让你\(l,r\)指针一直左右移动,就能够卡到\(O(n^2)\)我还不如写暴力算法
因此莫队的精髓来了,既然都是询问,那咱们是否能够经过适当地改变询问的顺序来让\(l,r\)跳转的幅度更小一点。数组
全部咱们能够利用分块的思想来优化:对于两个询问,若在其\(l\)在同块,那么将其\(r\)做为排序关键字,若\(l\)不在同块,就将\(l\)做为关键字排序(这就是双关键字)优化
这样就能够优化时间复杂度么,咱们看一下严格的证实(摘自大米饼的博客):spa
首先,枚举\(m\)个答案,就一个\(m\)了。设分块大小为\(unit\),元素\(i\)所属的快为\(blk_i\).net
分类讨论:指针
①\(l\)的移动:若下一个询问与当前询问的\(l\)所在的块不一样,那么只须要通过最多\(2\cdot unit\)步可使得\(l\)成功到达目标.复杂度为:\(O(m\cdot unit)\)code
②\(r\)的移动:\(r\)只有在\(blk_l\)相同时才会有序(其他时候仍是疯狂地乱跳,你知道,一提到乱跳,那么每一次最坏就要跳\(n\)次!),\(blk_l\)何时相同?在同一块里面\(blk_i\)相同。对于每个块,排序执行了第二关键字: \(r\)。因此这里面的\(r\)是单调递增的,因此枚举完一个块,\(r\)最多移动n次。总共有\(\frac{n}{unit}\)个块:复杂度为:\(O(\frac{n^2}{unit})\)htm
总结:\(O(n\cdot unit+\frac{n^2}{unit})\)(\(n,m\)同级,就统一使用\(n\))
根据基本不等式得:当\(unit=\sqrt n\)时,获得莫队算法的真正复杂度:\(O(n\sqrt n)\)
而后除此之外莫队还有一个更加NB的常数优化,即在cmp时写成:
return blk[a.l]<blk[b.l]||(blk[a.l]==blk[b.l]&&(blk[a.l]&1?a.r<b.r:a.r>b.r));
其实也很好理解吧,当左端点同块时判断一下当前快编号的奇偶性,尽可能让右端点波动范围较小(相似波浪形)
板子题参考:SPOJ D-query
大体题意:给定一个数组,每次询问一个区间内有多少不一样的元素
很简单的莫队板子题,对于每一次加入新的数时都判断一下这个数以前是否出现过便可,删除时同理。
别忘记离散化,CODE
#include<cstdio> #include<cctype> #include<cmath> #include<algorithm> using namespace std; const int N=30005,M=200005; struct data { int l,r,ans,id; }q[M]; int a[N],b[N],n,m,size,tot,blk[N],res,L,R,cnt[N]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline bool cmp1(data a,data b) { return blk[a.l]<blk[b.l]||(blk[a.l]==blk[b.l]&&(blk[a.l]&1?a.r<b.r:a.r>b.r)); } inline bool cmp2(data a,data b) { return a.id<b.id; } inline int find(int x) { int l=1,r=tot,mid; while (l<=r) { mid=l+r>>1; if (b[mid]==x) return mid; if (b[mid]<x) l=mid+1; else r=mid-1; } } inline void add(int col) { if (++cnt[col]==1) ++res; } inline void del(int col) { if (--cnt[col]==0) --res; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); size=sqrt(n); for (i=1;i<=n;++i) read(a[i]),b[i]=a[i],blk[i]=(i-1)/size+1; sort(b+1,b+n+1); tot=unique(b+1,b+n+1)-b-1; for (i=1;i<=n;++i) a[i]=find(a[i]); for (read(m),i=1;i<=m;++i) read(q[i].l),read(q[i].r),q[i].id=i; sort(q+1,q+m+1,cmp1); L=q[1].l; R=q[1].r; for (i=L;i<=R;++i) add(a[i]); q[1].ans=res; for (i=2;i<=m;++i) { while (L>q[i].l) add(a[--L]); while (L<q[i].l) del(a[L++]); while (R<q[i].r) add(a[++R]); while (R>q[i].r) del(a[R--]); q[i].ans=res; } for (sort(q+1,q+m+1,cmp2),i=1;i<=m;++i) write(q[i].ans),putchar('\n'); return 0; }