莫队
莫队
解决区间离线问题的离线算法。主要思想基于分块,时间复杂度是 \(O(n\sqrt n)\)的,但有可能被卡。
对询问进行分块,所以题目必须可以离线。
普通莫队
有三个关键词:挪动区间,按某种顺序,离线处理。
挪动区间
比如当前查询的区间是 \([l,r]\) ,第二次查询区间是 \([L,R]\),
可以将 \(l\to L,r \to R\) 挪动,在挪动过程中修改答案。
while(l>L) --l,add(l);//先减后加
while(r<R) ++r,add(r);//先加后删
while(l<L) del(l),++l;//先删后加
while(r>R) del(r),--r;//先删后减
模拟一下可得。
其中 add() 是扩展函数,del 是删除函数。
inline void add(int val) {//b是桶
int x=a[val];
c+=2*b[x]+1;//b[x]+1 对于平方和 C 的影响
b[x]++;
}
inline void del(int val) {
int x=a[val];
c-=2*b[x]-1;
b[x]--;
}
按某种顺序
把 \([1,n]\) 分成 \(\sqrt n\) 块。
先把区间按照左端点 \(l\) 所在的块从左往右排序。
再把 \(l\) 所在块相同的区间按 \(r\) 从小到大排序。
就是说,把 \(l\) 在一个块内的放一块,然后按照 \(r\) 排序。
这样相邻两个询问的左端点变化在 \(\sqrt n\) 内,而右端点是单调的,以上一次的回答为基础,那么每个块内只需要花费 \(O(\sqrt n)\) 移动左端点,而整块中右端点增长的范围之和是 \(O(n)\),所以在 \(O(n\sqrt n)\) 内解决问题。
离线处理
莫队是先把询问的区间存储起来再统一处理,当然是离线算法,最后把每次询问对应的答案用数组存储起来。
实现
main 函数里:
int b=sqrt(n);
for(int i=1;i<=b;++i) {
s[i]=(i-1)*b;
e[i]=i*b;
}
e[b]=n;
for(int i=1;i<=b;++i) {
for(int j=s[i];j<=e[i];++j) {
bel[j]=i;
}
}
sort(p+1,p+m+1,cmp);
int cmp(node x,node y){
if(bel[x.l]==bel[y.l]) return x.r<y.r;
return x.l<y.l;
}
例题
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int n,m,k,l=1,r,c,L,R,B,s[N],e[N],a[N],bel[N],b[N],ans[N];//l 初值为1
inline int read(){
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node{
int l,r,id;
}p[N];
inline void add(int val) {//b是桶
int x=a[val];
c+=2*b[x]+1;//b[x]+1 对于平方和 C 的影响
b[x]++;
}
inline void del(int val) {
int x=a[val];
c-=2*b[x]-1;
b[x]--;
}
int cmp(node x,node y){
if(bel[x.l]==bel[y.l]) return x.r<y.r;
return x.l<y.l;
}
int main(){
n=read(); m=read(); k=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i) {
p[i].l=read(); p[i].r=read();
p[i].id=i;
}
B=sqrt(n);
for(int i=1;i<=B;++i) {
s[i]=(i-1)*B;
e[i]=i*B;
}
e[B]=n;
for(int i=1;i<=B;++i) {
for(int j=s[i];j<=e[i];++j) {
bel[j]=i;
}
}
sort(p+1,p+1+m,cmp);
for(int i=1;i<=m;++i) {
L=p[i].l; R=p[i].r;
while(l>L) --l,add(l);//先减后加
while(r<R) ++r,add(r);//先加后删
while(l<L) del(l),++l;//先删后加
while(r>R) del(r),--r;//先删后减
ans[p[i].id]=c;
}
for(int i=1;i<=m;++i) {
cout<<ans[i]<<'\n';
}
return 0;
}

浙公网安备 33010602011771号