莫队总结

莫队总结

离线莫队

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等可能会炸。

板子(SP326 DQUERY - D-query):

// 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;
}

posted @ 2019-07-26 15:01  Santiego  阅读(123)  评论(0编辑  收藏  举报