莫队总结
莫队总结
离线莫队
for(int i=1;i<=m;++i){
int ql=q[i].l,qr=q[i].r;
while(l<ql) del(l++);
while(l>ql) add(--l);
while(r<qr) add(++r);
while(r>qr) del(r--);
qans[q[i].qid]=ans;
}
离线处理询问,暴力按一定顺序处理区间询问,这样我们可以利用上一次询问得到的信息来处理当前询问,大大加快速度。
我们一般先按每块\(\sqrt{n}\)大小对各询问区间左端点分块编号(其中\(n\)为总区间大小,\(m\)为询问次数,注意随机数据时\(\frac{n}{\sqrt{m\times\frac{2}{3}}}\)更优),同一块内的按右端点排序,否则按左端点,按排序后的顺序处理询问,总复杂度为\(O(n\times\sqrt{n})\)。
这里还有一种卡常排序方法比上述排序方式更优:
inline bool cmp(nod a, nod b){
return ((a.bid^b.bid)?(a.l<b.l):((a.bid&1)?a.r<b.r:a.r>b.r));
}
具体原理:
这样能快是因为指针移到右边后不用再跳回左边,而跳回左边后处理下一个块又要跳回右边,这样能减少一半操作,理论上能快一倍
——莫队算法初探
各询问区间转移必须\(O(1)\),否则会T爆,所以只要能想出\(O(1)\)的方式转移\([l,r]\),莫队就不难了。
另外莫队小心初始化,直接l=0,r=0
等可能会炸。
// luogu-judger-enable-o2
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int n,block,a[500005];
int cnt[1000010],ans,l,r;
inline void add(int x){
ans+=(++cnt[a[x]]==1);
}
inline void del(int x){
ans-=(--cnt[a[x]]==0);
}
struct nod{
int l,r,qid,bid;
} q[500005];
inline bool cmp(nod a, nod b){
return ((a.bid^b.bid)?a.l<b.l:((a.bid&1)?a.r<b.r:a.r>b.r));
}
int qans[500005];
inline int read(){
char ch;int s=0;
ch = getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s;
}
int main()
{
n=read();
for(int i=1;i<=n;++i) a[i]=read();
int m=read();
block=n/sqrt(m*2/3);
for(int i=1;i<=m;++i){
q[i].l=read(),q[i].r=read();
q[i].qid=i;
q[i].bid=q[i].l/block;
}
sort(q+1, q+1+m, cmp);
l=1,r=0;
for(int i=1;i<=m;++i){
int ql=q[i].l,qr=q[i].r;
while(l<ql) del(l++);
while(l>ql) add(--l);
while(r<qr) add(++r);
while(r>qr) del(r--);
qans[q[i].qid]=ans;
}
for(int i=1;i<=m;++i) printf("%d\n", qans[i]);
return 0;
}