带修莫队学习笔记
初步认识 \(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;
}

浙公网安备 33010602011771号