莫队-带修莫队(学习笔记)
P1903 [国家集训队] 数颜色 / 维护队列
简述:
我们发现莫队的操作是离线的,且只能经行查询工作,但如果遇到需要修改的题目该如何办。这时候就需要用带修莫队来维护了,带修莫队也是一种离线算法,复杂度也约等于 \(O(n\sqrt n)\)
原理:
我们可以在维护一个修改的时间刻。也就是修改的时间给储存下来,这样在我们移动区间后将现在的时间移到对应的时间刻,同时对修改的内容进行参数的调整即可。在查询是维护一个当前时间,若时间过了就回退,若还没到就增加
为了保证若时间回退修改的值复原,那我们可以在修改时将修改值和当前值经行交换,这样在回退时修改就是变回原样
void change(int x){
if(u[x].id>=l && u[x].id<=r){
del(a[u[x].id]);
add(u[x].val);
}
swap(a[u[x].id],u[x].val);
}
需要注意的是,由于有了时间的增减,普通的 \(\sqrt n\) 的块大小不太适用,一般来说 \(n^{\frac{2}{3}}\)或者以下大小:
int len=10;
while(1ll*len*len*len<=1ll*n*n*3)len++;
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=135000;
int a[N],block[N],ans[N],vis[N*10];
struct query{
int l,r,t,id;
}q[N];
struct update{
int id,val;
}u[N];
bool cmp(query x,query y){
if(block[x.l]!=block[y.l])return x.l<y.l;
if(block[x.r]!=block[y.r])return (block[x.l]&1)?(x.r<y.r):(x.r>y.r);
return x.t<y.t;
}
int sum,l,r,now;
void add(int x){
if(!vis[x])sum++;
vis[x]++;
}
void del(int x){
vis[x]--;
if(!vis[x])sum--;
}
void change(int x){
if(u[x].id>=l && u[x].id<=r){
del(a[u[x].id]);
add(u[x].val);
}
swap(a[u[x].id],u[x].val);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int len=10;
while(1ll*len*len*len<=1ll*n*n*3)len++;
for(int i=1;i<=n;i++){
block[i]=i/len;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int cntq=0,cntu=0;
for(int i=1;i<=m;i++){
char op;
int l,r;
cin>>op>>l>>r;
if(op=='Q'){
q[++cntq]={l,r,cntu,cntq};
}
else{
u[++cntu]={l,r};
}
}
sort(q+1,q+cntq+1,cmp);
l=1,r=0;
now=0;
for(int i=1;i<=cntq;i++){
int ll=q[i].l,rr=q[i].r;
while(now<q[i].t)change(++now);
while(now>q[i].t)change(now--);
while(l<ll)del(a[l++]);
while(l>ll)add(a[--l]);
while(r<rr)add(a[++r]);
while(r>rr)del(a[r--]);
ans[q[i].id]=sum;
}
for(int i=1;i<=cntq;i++){
printf("%d\n",ans[i]);
}
}
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682412
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步