bzoj2120 数颜色 (带修莫队模板题)

问题描述

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

Input

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

Output

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

Sample Input

6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6

Sample Output

4
4
3
4

Hint

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

分析:

带修改莫队算法的模板题,只支持单点修改
基本思路和普通莫队一样,但是每个离线保存的询问多加了一个询问前的修改个数(修改时间?)。

从4个while变成了6个(多了修改的两个)

代码少量注释

code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e6+5;
int a[maxm];
struct Query{//保存询问
    int l,r,x,id;//当前查询之前修改的个数
    Query(){};
    Query(int a,int b,int c,int d){
        l=a,r=b,x=c,id=d;
    }
}q[maxm];
struct Change{//保存修改
    int x,y;//x是修改的位置,y是修改的值
    Change(){};
    Change(int xx,int yy){
        x=xx,y=yy;
    }
}c[maxm];
int n,m;
int numq,numc;
int block;
int res[maxm],ans;
int cnt[maxm];
bool cmp(Query a,Query b){
    if(a.l/block!=b.l/block){//按左端点所在块排序
        return a.l<b.l;
    }else if(a.r/block!=b.r/block){//按右端点所在块排序
        return a.r<b.r;
    }else{//修改个数排序
        return a.x<b.x;
    }
}
void add(int x){
    cnt[a[x]]++;
    if(cnt[a[x]]==1)ans++;
}
void del(int x){
    cnt[a[x]]--;
    if(cnt[a[x]]==0)ans--;
}
void change(int x,int l,int r){
    if(c[x].x>=l&&c[x].x<=r){
        del(c[x].x);//删除以前的
    }
    swap(a[c[x].x],c[x].y);//如果不在范围内只改值,不修改cnt[]
    //swap的话,可以想象到如果向右移,交换一次,然后向左移回去,就换回来了
    if(c[x].x>=l&&c[x].x<=r){
        add(c[x].x);//改成新的
    }
}
void solve(){
    int l=q[1].l,r=l-1,now=0;//now是上一个修改的时间点,不是q[1].x-1,而是numc-1
    for(int i=1;i<=numq;i++){
        int ql=q[i].l,qr=q[i].r;
        int x=q[i].x;
        while(l>ql)add(--l);
        while(l<ql)del(l++);
        while(r>qr)del(r--);
        while(r<qr)add(++r);
        while(now<x)change(++now,l,r);
        while(now>x)change(now--,l,r);
        res[q[i].id]=ans;
    }
    for(int i=1;i<=numq;i++){
        printf("%d\n",res[i]);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    block=sqrt(n*1.0);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=m;i++){
        char d[2];
        int x,y;
        scanf("%s%d%d",d,&x,&y);
        if(d[0]=='Q'){
            numq++;
            q[numq]=Query(x,y,numc,numq);
        }else{
            numc++;
            c[numc]=Change(x,y);
        }
    }
    sort(q+1,q+1+numq,cmp);
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值