20.莫队
莫队
引入
当如果知道区间
的答案,如果能 地求出 相邻的区域的答案,那么,就可以考虑莫队
莫队算法
离线 暴力 分块
莫队时一种离线算法,他通常用于不修改只查询的一类区间问题,复杂度为
思考一个问题,
给定一个序列,和
考虑暴力做法,我们定义两个指针
我们可以从
显然,我们可以造一组恶心的数据把这样的想法卡死,每次右节点跑
暴力法几何解释
我们考虑用几何来解释区间和区间之间的连线,我们把区间

莫队算法
把数组分块,然后把查询的区间按左端点所在的块排序,如果左端点相同,再按右端点排序
莫队算法的几何解释
莫队算法把

考虑
考虑
两者相加,总时间复杂度为
#include<bits/stdc++.h>
using namespace std;
const int MAXX=1e6;
struct node{
int L,R,id;
node(int L=0,int R=0,int id=0):L(L),R(R),id(id){}
};
int now_ans=0;
vector<int> a,belong,ans,cnt;
vector<node> q;
bool cmp(node a,node b){
if(belong[a.L]!=belong[b.L]) return belong[a.L]<belong[b.L];
return a.R<b.R;
}
void add(int x){cnt[a[x]]++;if(cnt[a[x]]==1) now_ans++;}
void del(int x){cnt[a[x]]--;if(cnt[a[x]]==0) now_ans--;}
int main(){
freopen("P1972.in","r",stdin);
freopen("P1972.out","w",stdout);
int n;scanf("%d",&n);
int block=sqrt(n),t=n/block+(n%block!=0);
a.assign(n+1,0);belong.assign(n+1,0);ans.assign(n+1,0);cnt.assign(MAXX+1,0);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),belong[i]=(i-1)/block+1;
int m;scanf("%d",&m);q.assign(m+1,node());
for(int i=1;i<=m;i++){
int L,R;scanf("%d%d",&L,&R);
q[i]=node(L,R,i);
}
sort(q.begin()+1,q.begin()+1+m,cmp);
int L=1,R=0;
for(int i=1;i<=m;i++){
while(L<q[i].L) del(L++);
while(R>q[i].R) del(R--);
while(L>q[i].L) add(--L);
while(R<q[i].R) add(++R);
ans[q[i].id]=now_ans;
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
莫队可以有些小优化,就是对于编号为奇数的块按照
这个优化在图上也很显然能表示出来,从奇数点
带修改的莫队
如果上面那个问题再添加一个修改操作
R x val 表示把第
我们可以按照修改把询问
不修改的是二维平面上的点

就变成了在三维平面上选一条路径使得曼哈顿距离尽量小
依旧采用分块的办法,先按照左端点
那么块的大小取多少最好呢,不妨设块的大小为
方向上的复杂度。 在一个区块内,沿着 方向一次最多移动 ,所有的区块公用 次移动,总计算量为 方向上的复杂度。和 一样,总复杂度为 方向上的复杂度,,每个被 和 区块限制的方格内,沿着 方向单向移动最多 次(询问的次数)总共有 个方格,总计算量为
总时间复杂度为