带修莫队学习笔记

模版题传送门~

初步认识 \(Intro\)

在普通莫队中,我们会被要求求一些无法快速合并但能快速插入删除的信息,这时我们就会将其离线处理,在区间内反复横跳。

但是有些问题会既包含修改,也难以快速合并信息,这时不管是普通数据结构还是莫队都不好使了,于是便有了带修改的莫队,带修改的莫队和普通莫队一样,是离线算法,通过在提问排序后不断移动指针实现。

算法原理 \(Principle\)

带修莫队作为离线算法,不光要记录提问,也要记录修改操作,每个提问和修改都会带有一个时间参数。普通莫队中的移动 \(l\)\(r\) 指针以求答案变为了移动 \(l\)\(r\)\(t\) 三个指针。

在对提问的询问中采用先比较 \(l\)\(r\) 所在的块再比较 \(t\)。注意不同于普通莫队,在这里块长被设为 $n^{\frac{2}{3} } $ 。

加入与删除 \(Add\) \(and\) \(Delete\)

在空间上的加入与删除与普通莫队相同,这里就不多赘述了

时间上若到了一个有时间修改的点,如果是加入,则将原来的值修改为新值,否则就撤销原来的操作。

代码 \(Code\)

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

#define int long long
const int N = 133340;	//数据量最大值
const int MAX = 1e6+2;	//值域最大值
int blk;
typedef struct Qry{
	int l, r, t;
	int idx;
	friend bool operator < (Qry a, Qry b){	//排序
		if(a.l/blk == b.l/blk){
			if(a.r/blk == b.r/blk)
				return a.t < b.t;
			return a.r/blk < b.r/blk;
		}
		return a.l/blk < b.l/blk;
	}
}Qry;
Qry q[N];
typedef struct{	//记录原来的数据以及要改成的数据 方便撤销
	int x;
	int frm;
	int val;
}Node;
Node cg[N];
int a[N];
int cnt[MAX];	//注意值域是1e6不是133333 这里调了一个小时🤡
int ans[N];
int res = 0;
//同普通莫队
inline void add(int x){
	if(!cnt[x])
		res++;
	cnt[x]++;
}
inline void del(int x){
	if(cnt[x] == 1)
		res--;
	cnt[x]--;
}
//更新时间
inline void add_time(int x, int l, int r){
	if(!cg[x].x)
		return;
	a[cg[x].x] = cg[x].val;
	if(cg[x].x >= l && cg[x].x <= r){
		del(cg[x].frm);		//删除原来的数据
		add(cg[x].val);		//加上新的数据
	}
}
inline void del_time(int x, int l, int r){	//撤销 相反于上
	if(!cg[x].x)
		return;
	a[cg[x].x] = cg[x].frm;
	if(cg[x].x >= l && cg[x].x <= r){
		del(cg[x].val);
		add(cg[x].frm);
	}
}
signed main(){
	int n, m;
	cin >> n >> m;
	int b[n+2];
	for(int i = 1; i <= n; i++)
		cin >> a[i], b[i] = a[i];
	blk = pow(n, 0.666);	//用n的2/3次方为块长
	int ct = 0;
	for(int i = 1; i <= m; i++){
		char cmd;
		cin >> cmd;
		cg[i].x = 0;
		if(cmd == 'Q'){
			ct++;
			cin >> q[ct].l >> q[ct].r;
			q[ct].t = i;
			q[ct].idx = ct;
		}
		else{
			cin >> cg[i].x >> cg[i].val;
			cg[i].frm = b[cg[i].x];
			b[cg[i].x] = cg[i].val;
		}
	}
	sort(q+1, q+1+ct);	//排序
	int l = 1, r = 0, t = 0;
	for(int i = 1; i <= ct; i++){
		//调整左右边界(普通莫队)
		while(l > q[i].l)
			add(a[--l]);
		while(l < q[i].l)
			del(a[l++]);
		while(r > q[i].r)
			del(a[r--]);
		while(r < q[i].r)
			add(a[++r]);
		//调整时间
		while(t < q[i].t)
			add_time(++t, l, r);
		while(t > q[i].t)
			del_time(t--, l, r);
		ans[q[i].idx] = res;
	}
	for(int i = 1; i <= ct; i++)	//离线输出
		cout << ans[i] << endl;
	return 0;
}
posted @ 2024-06-11 17:17  HurryCine  阅读(21)  评论(0)    收藏  举报