[BZOJ 2120]数颜色:带修改莫队

点击这里查看原题

与普通莫队相比,本题需要单点修改,因此需要分别记录查询和修改两种操作。
修改操作需要记录:

  • pos:修改的位置
  • x:修改前的值

查询操作需要记录:

  • p:上一个修改操作的序号
  • id:在输入数据中的顺序
  • l,r:查询的区间

查询操作依然是按块排序,只不过每次查询时要处理好修改操作。
用一个vis数组记录该位置是否在目前的区间内,如果要修改一个在队中的位置,需要先出队,然后修改该位置的值,再入队。如果不在队中直接修改即可。

/*
User:Small
Language:C++
Problem No.:2120
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
const int M=1e4+5; 
int n,m,a[M],num[M*100],res,tot,cnt,pos[M],ans[M],b[M];
bool vis[M];
struct op1{
    int pos,x,y;
}ch[M];
struct op2{
    int pre,l,r,id;
}qu[M];
bool cmp(op2 a,op2 b){
    return pos[a.l]!=pos[b.l]?pos[a.l]<pos[b.l]:a.r<b.r;
}
void update(int x){
    if(vis[x]){
        num[a[x]]--;
        if(num[a[x]]==0) res--;
    }
    else{ 
        num[a[x]]++;
        if(num[a[x]]==1) res++;
    }
    vis[x]^=1;
}
void change(int x,int y){
    if(vis[x]){
        update(x);
        a[x]=y;
        update(x);
    }
    else a[x]=y;
}
void solve(){
    int l=1,r=0,now=0;
    for(int i=1;i<=cnt;i++){
        for(int j=now+1;j<=qu[i].pre;j++)
            change(ch[j].pos,ch[j].y);
        for(int j=now;j>qu[i].pre;j--)
            change(ch[j].pos,ch[j].x);
        for(int j=r+1;j<=qu[i].r;j++) update(j);
        for(int j=r;j>qu[i].r;j--) update(j);
        for(int j=l-1;j>=qu[i].l;j--) update(j);
        for(int j=l;j<qu[i].l;j++) update(j);
        ans[qu[i].id]=res;
        l=qu[i].l,r=qu[i].r,now=qu[i].pre;
    }
}
int main(){
    freopen("data.in","r",stdin);//
    scanf("%d%d",&n,&m);
    int t=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
        pos[i]=(i-1)/t+1;
    }
    for(int i=1;i<=m;i++){
        char op[5];
        int x,y;
        scanf("%s%d%d",op,&x,&y);
        if(op[0]=='R'){
            ch[++tot]=(op1){x,b[x],y};
            b[x]=y;
        }
        else qu[++cnt]=(op2){tot,x,y,cnt};
    }
    sort(qu+1,qu+cnt+1,cmp);
    solve();
    for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值