参考博客:莫队算法-从入门到黑题
带修改的莫队:离线修改区间查询,在原来的基础上再加上一个“时间戳”,而查询操作的时间戳沿用之前最近的修改操作的时间戳。跑主算法时定义当前时间戳为 time ,对于每个查询操作,如果当前时间戳相对太大了,说明已进行的修改操作比要求的多,就把之前改的改回来,反之往后改。只有当当前区间和查询区间左右端点、时间戳均重合时,才认定区间完全重合,此时的答案才是本次查询的最终答案。
就是再弄一指针,在修改操作上跳来跳去,如果当前修改多了就改回来,改少了就改过去,直到次数恰当为止。
这样,我们当前区间的移动方向从四个 ( [ l − 1 , r ] 、 [ l + 1 , r ] 、 [ l , r − 1 ] 、 [ l , r + 1 ] ) ([l−1,r]、[l+1,r]、[l,r−1]、[l,r+1]) ([l−1,r]、[l+1,r]、[l,r−1]、[l,r+1])变成了六个 ( [ l − 1 , r , t ] 、 [ l + 1 , r , t ] 、 [ l , r − 1 , t ] 、 [ l , r + 1 , t ] 、 [ l , r , t − 1 ] 、 [ l , r , t + 1 ] ) ([l−1,r,t]、[l+1,r,t]、[l,r−1,t]、[l,r+1,t]、[l,r,t−1]、[l,r,t+1]) ([l−1,r,t]、[l+1,r,t]、[l,r−1,t]、[l,r+1,t]、[l,r,t−1]、[l,r,t+1])
带修改的莫队排序
相比于普通的莫队来说,加了个“时间戳”
bool cmp(node a1,node a2) {
if(pos[a1.l] != pos[a2.l]) return pos[a1.l] < pos[a2.l];
else if(pos[a1.r] != pos[a2.r]) return pos[a1.r] < pos[a2.r];
else return a1.time < a2.time;
}
指针移动
移动完左右端点后,判断当前的时间节点是否等于当前的查询的区间时间点。
void work() {
int l = 0, r = 0,now = 0,time = 0;
for(int i=1;i<=m;i++) {
int ql = pen[i].l;
int qr = pen[i].r;
int qt = pen[i].time;
while(l < ql) {
cnt[a[l]] --;
if(cnt[a[l]] == 0) now --;
l ++;
}
while(l > ql) {
l --;
cnt[a[l]] ++;
if(cnt[a[l]] == 1) now ++;
}
while(r < qr) {
r ++;
cnt[a[r]] ++;
if(cnt[a[r]] == 1) now ++;
}
while(r > qr) {
cnt[a[r]] --;
if(cnt[a[r]] == 0) now --;
r --;
}
while(time < qt) {
time ++;
int p = color[time].p;
int c = color[time].c;
if(p >= l && p <= r) {
cnt[a[p]] --;
if(cnt[a[p]] == 0) now --;
cnt[c] ++;
if(cnt[c] == 1) now ++;
}
swap(a[p],color[time].c);
}
while(time > qt) {
int p = color[time].p;
int c = color[time].c;
if(p >= l && p <= r) {
cnt[a[p]] --;
if(cnt[a[p]] == 0) now --;
cnt[c] ++;
if(cnt[c] == 1) now ++;
}
swap(a[p],color[time].c);
time --;
}
res[pen[i].id] = now;
}
}
题意:购买了一套 N N N 支彩色画笔(其中有些颜色可能相同),摆成一排。存在以下操作
-
Q L R Q\ L\ R Q L R 代表询问你从第 L L L 支画笔到第 R R R 支画笔中共有几种不同颜色的画笔。
-
R P C o l R\ P\ Col R P Col 把第 P P P 支画笔替换为颜色 C o l Col Col。
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
struct node{
int l,r;
int time,id;
}pen[4 * N];
struct Color{
int p,c;
int time;
}color[N];
int pos[N],a[N],res[N],cnt[N];
int n,m;
bool cmp(node a1,node a2) {
if(pos[a1.l] != pos[a2.l]) return pos[a1.l] < pos[a2.l];
else if(pos[a1.r] != pos[a2.r]) return pos[a1.r] < pos[a2.r];
else return a1.time < a2.time;
}
void work() {
int l = 0, r = 0,now = 0,time = 0;
for(int i=1;i<=m;i++) {
int ql = pen[i].l;
int qr = pen[i].r;
int qt = pen[i].time;
while(l < ql) {
cnt[a[l]] --;
if(cnt[a[l]] == 0) now --;
l ++;
}
while(l > ql) {
l --;
cnt[a[l]] ++;
if(cnt[a[l]] == 1) now ++;
}
while(r < qr) {
r ++;
cnt[a[r]] ++;
if(cnt[a[r]] == 1) now ++;
}
while(r > qr) {
cnt[a[r]] --;
if(cnt[a[r]] == 0) now --;
r --;
}
while(time < qt) {
time ++;
int p = color[time].p;
int c = color[time].c;
if(p >= l && p <= r) {
cnt[a[p]] --;
if(cnt[a[p]] == 0) now --;
cnt[c] ++;
if(cnt[c] == 1) now ++;
}
swap(a[p],color[time].c);
}
while(time > qt) {
int p = color[time].p;
int c = color[time].c;
if(p >= l && p <= r) {
cnt[a[p]] --;
if(cnt[a[p]] == 0) now --;
cnt[c] ++;
if(cnt[c] == 1) now ++;
}
swap(a[p],color[time].c);
time --;
}
res[pen[i].id] = now;
}
}
int main(){
scanf("%d %d",&n,&m);
int t = pow(n,2.0/3.0);
int size = ceil((double)n / t);
for(int i=1;i<=size;i++) {
for(int j=(i-1)*t+1;j<=min(i*t,n);j++) {
pos[j] = i;
}
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int L = 0,k = 0;
for(int i=1;i<=m;i++) {
char s[2] = {0};
scanf("%s",s);
if(s[0] == 'Q') {
k ++;
scanf("%d %d",&pen[k].l,&pen[k].r);
pen[k].time = L;
pen[k].id = k;
}
else {
L ++;
scanf("%d %d",&color[L].p,&color[L].c);
color[L].time = L;
}
}
sort(pen+1,pen+1+k,cmp);
m = k;
work();
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return 0;
}