(笔记)莫队 回滚莫队 树上莫队

算法特点:

\(O(n\sqrt n)\),离线,多数时候需要一点卡常寄巧(但其时间复杂度在完全随机的数据下还是较优秀的)(?),不能做强制在线(这不废话吗),以根号分块为基础,主体为指针操作。

基础莫队

P1972 [SDOI2009] HH的项链

rt,以前是可以用莫队做的,但是现在加入了加强数据,好像不行了。

[ABC174F] Range Set Query

所以我们找了一题一样但是没有卡常的。

题意大概是给定\(A[1]\)~\(A[N]\),每次有询问\(l,r\),问区间\([l,r]\)中有多少个不同的数。

1.分块

块长为\(\sqrt n\),对询问左端点分块后对操作进行排序,第一关键字为操作左端点块的升序编号,第二关键字为操作右端点降序。

2.主体

对本题,令\(cnt[i]\)表示\(i\)出现的次数。使左指针\(le\)初始为\(1\),右指针\(ri\)\(0\)。对排好序的操作进行指针对齐,由于之前排好序,所以在\(m,n\)同阶时,其时间复杂度为\(O(n\sqrt n)\)

形象化即分块后右端点是有序的,每个块最多执行\(n\)次收缩操作,由\(\sqrt n\)块得最多进行\(n\sqrt n\)

考虑左端点,每次在块内移动距离不超过\(\sqrt n\), 共\(m\)次移动,共移动\(m\sqrt n\)次,垮块的移动最多移动距离\(2\sqrt n\),最多垮\(\sqrt n\)次,故左端点共移动\(m\sqrt n + 2n\)次 ,即\(O(m\sqrt n)\)

总体时间复杂度在两者同阶时即为\(O(n\sqrt n)\)

除此,当\(m,n\)不同阶时,使块长为\(\sqrt\frac{n^2}{m}\)时较优,具体证明可以通过式子\(\frac{n^2m}{s}=mn^2\)用基本不等式最小值推出\(ms=\frac{n^2}{s}\)时最小,整理上式可以得到\(s=\sqrt\frac{n^2}{m}\)

代码贴贴:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,a[N],ans[N],bs,cnt[N],temp,b[N];
struct Q{
	int l,r,id;
}q[N];
bool cmp(Q x,Q y){
	return b[x.l]==b[y.l] ? x.r<y.r:b[x.l]<b[y.l];
}
void add(int x){
	cnt[a[x]]++;
	if(cnt[a[x]]==1)temp++;
}
void del(int x){
	cnt[a[x]]--;
	if(cnt[a[x]]==0)temp--;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	bs=sqrt(n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=(i-1)/bs+1;
	}
	for(int i=1;i<=m;i++){
		cin>>q[i].l>>q[i].r;
		q[i].id=i;
	}
	sort(q+1,q+1+m,cmp);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(r<q[i].r)add(++r);
		while(r>q[i].r)del(r--);
		while(l<q[i].l)del(l++);
		while(l>q[i].l)add(--l);
		ans[q[i].id]=temp;
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
	return 0;
} 

回滚莫队

用于解决:

只减不加(如\(mex\)值,区间内未出现过的最小非负整数),只加不减(如区间最大值)

具体前者比后者要复杂,但总体思想相同。

例题

只加不减歴史の研究

只减不加P4137 Rmq Problem / mex

其余待更新

posted @ 2025-04-24 14:51  TBSF_0207  阅读(3)  评论(0)    收藏  举报