莫队

莫队

介绍

莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。

普通莫队算法

形式

假设 \(n=m\),那么对于序列上的区间询问问题,如果从 \(\left[l,r\right]\) 的答案能够 \(O(1)\) 扩展到 \(\left[l,r+1\right],\left[l+1,r\right],\left[l,r-1\right],\left[l-1,r\right]\) 即与 \(\left[l,r\right]\) 相邻的区间)的答案,那么可以在 \(O(n\sqrt n)\) 的复杂度内求出所有询问的答案。

例题

P1494

实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
int ans[N],cnt[N],t,c[N];
int fans[N][3],ans1 = 0,ans2 = 0;
struct node{
	int l,r,id;
}a[N];
int gcd(int x,int y){
	return (y == 0)?x:gcd(y,x%y);
}
bool cmp(node x,node y){
	if(x.l/t == y.l/t){
		return x.r < y.r;
	}else{
		return x.l < y.l;
	}
}
void add(int x){
	ans2 = ((int)sqrt(ans2)+1ll) * ((int)sqrt(ans2)+2ll);
	ans1 -= cnt[c[x]] * (cnt[c[x]]-1ll);
	cnt[c[x]] ++;
	ans1 += cnt[c[x]] * (cnt[c[x]]-1ll);
	return;
}
void del(int x){
	ans2 = ((int)sqrt(ans2)) * ((int)sqrt(ans2)-1ll);
	ans1 -= cnt[c[x]] * (cnt[c[x]]-1ll);
	cnt[c[x]] --;
	ans1 += cnt[c[x]] * (cnt[c[x]]-1ll);
	return;
}
signed main(){
// 	freopen("P1494_2.in","r",stdin);
	int n,m;
	scanf("%lld%lld",&n,&m);
	t = sqrt(n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&c[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i].l,&a[i].r);
		a[i].id = i;
//		cout<<i;
	}
	sort(a+1,a+1+m,cmp);
	int l = 1,r = 0;
	for(int i=1;i<=m;i++){
		while(r < a[i].r){
			add(++r);
		}
		while(l > a[i].l){
			add(--l);
		}
		while(r > a[i].r){
			del(r--);
		}
		while(l < a[i].l){
			del(l++);
		}
		fans[a[i].id][1] = ans1;
		fans[a[i].id][2] = ((int)sqrt(ans2)) * ((int)sqrt(ans2)-1);;
	}
	for(int i=1;i<=m;i++){
		int g = __gcd(fans[i][1],fans[i][2]);
		if(g == 0){
			printf("0/1\n");
			continue;
		}
		printf("%lld/%lld\n",fans[i][1]/g,fans[i][2]/g);	
	}
	return 0;
} 

优化

奇偶化排序优化,无 \(O2\),实测运行时间为 1.00s->727ms

bool cmp(node x,node y){
	if(x.l/t != y.l/t){
		return x.l < y.l;
	}else if(x.l/t & 1){
		return x.r < y.r;
	}else{
		return x.r > y.r;
	}
}

带修改莫队

询问由 \([l,r]\) 变为 \([l,r,time]\),所以多了一个扩展维度:

  • \([l+1,r,time]\)
  • \([l-1,r,time]\)
  • \([l,r+1,time]\)
  • \([l,r-1,time]\)
  • \([l,r,time+1]\)
  • \([l,r,time-1]\)

例题

P1903

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int c[N],t,fans[N];
int c_cnt[N],ans = 0;
struct node{
	int l,r,time,id;
}a[N];
struct change_node{
	int x,c;
}b[N];
bool cmp(node x,node y){
	if(x.l/t == y.l/t){
		if(x.r/t == y.r/t){
			return x.time < y.time;
		}else{
			return x.r < y.r; 
		}
	}else{
		return x.l < y.l;
	}
}
void add(int x){
	if(c_cnt[c[x]] == 0){
		ans ++;
	}
	c_cnt[c[x]] ++;
}
void del(int x){
	c_cnt[c[x]] --;
	if(c_cnt[c[x]] == 0){
		ans --;
	}
}
int main(){
	int n,m,cntq = 0,cntr = 0;
	scanf("%d%d",&n,&m);
	t = pow(n,2.0/3.0);
	for(int i=1;i<=n;i++){
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=m;i++){
		char opt;
		std::cin>>opt;
		if(opt == 'Q'){
			cntq ++;
			scanf("%d%d",&a[cntq].l,&a[cntq].r);
			a[cntq].id = cntq;
			a[cntq].time = cntr;
		}else if(opt == 'R'){
			cntr ++;
			scanf("%d%d",&b[cntr].x,&b[cntr].c);
		}
	}
	sort(a+1,a+1+cntq,cmp);
	int l = 1,r = 0,now = 0;
	for(int i=1;i<=cntq;i++){
		while(l > a[i].l){
			add(--l);
		}
		while(r < a[i].r){
			add(++r);
		}
		while(l < a[i].l){
			del(l++); 
		}
		while(r > a[i].r){
			del(r--);
		}
		while(now < a[i].time){
			now ++;
			if(l <= b[now].x && r >= b[now].x){
				del(b[now].x);
				swap(c[b[now].x],b[now].c);
				add(b[now].x);
			}else{
				swap(c[b[now].x],b[now].c);
			}
		}
		while(now > a[i].time){
			if(l <= b[now].x && r >= b[now].x){
				del(b[now].x);
				swap(c[b[now].x],b[now].c);
				add(b[now].x);
			}else{
				swap(c[b[now].x],b[now].c);
			}
			now --;
		}
		fans[a[i].id] = ans;
	}
	for(int i=1;i<=cntq;i++){
		printf("%d\n",fans[i]);
	}
}
posted @ 2024-02-16 08:45  WhileTureRP++  阅读(11)  评论(0编辑  收藏  举报