「BZOJ2120」数颜色【带修莫队】

2120: 数颜色

Time Limit: 6 Sec Memory Limit: 259 MB
Submit: 11224 Solved: 4658
[Submit][Status][Discuss]

Description

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

Input

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

Output

对于每一个 Q u e r y Query Query的询问,你需要在对应的行中给出一个数字,代表第 L L L支画笔到第 R R 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 % 100\% 100%的数据, N ≤ 10000 N\leq 10000 N10000 M ≤ 10000 M\leq 10000 M10000,修改操作不多于 1000 1000 1000次,所有的输入数据中出现的所有整数均大于等于 1 1 1且不超过 1 0 6 10^6 106

题意

  • 在普通的莫队典型例题:查询区间颜色种类数量的基础上,需要支持单点颜色修改操作

题解

  • 带修莫队模版题
  • 每个查询操作记录一个值表示前面有多少次修改,然后对于每一个修改,记录这个点修改前的颜色和修改后的颜色,方便加上这个颜色和撤销

代码

#include<bits/stdc++.h>

using namespace std;
const int maxn=10005;      // max node
const int maxm=10005;      // max operate/query
const int max_col=1000005; // max color

int ans[maxm],a[maxn]; //a:initial cilor
int block_size,block[maxn],numq,numo,color[maxn],num[max_col],res; //color临时存每个位置的颜色
struct query{                                                      //numq:number of query   numo:number of operate
    int id,l,r,change;                                             //num:每个颜色的数量
    query(int a=0,int b=0,int c=0,int d=0) {                       //res:当前区间答案
        id=a;l=b;r=c;change=d;
    }
    friend bool operator<(const query&a,const query&b){
        if(block[a.l]^block[b.l]) return a.l<b.l;
        if(block[a.r]^block[b.r]) return a.r<b.r;
        return a.change<b.change;
    }
}q[maxm];

struct operate{
    int pos,pre,nxt;  //pos记录修改的位置,pre为修改之前的数,nxt为修改后的数
    operate(int a=0,int b=0,int c=0) {
        pos=a;pre=b;nxt=c;
    }
}o[maxm];

void init(int n)
{
    numq=numo=0;
    block_size=(int)pow(n,2.0/3);
    for(int i=1;i<=n;i++) block[i]=i/block_size;
}

void add_change(int id,int l,int r)
{
    if(o[id].pos>=l&&o[id].pos<=r) {
        if(--num[a[o[id].pos]]==0) res--;
        if(++num[o[id].nxt]==1) res++;
    }
    a[o[id].pos]=o[id].nxt;
}

void del_change(int id,int l,int r)
{
    if(o[id].pos>=l&&o[id].pos<=r) {
        if(--num[a[o[id].pos]]==0) res--;
        if(++num[o[id].pre]==1) res++;
    }
    a[o[id].pos]=o[id].pre;
}

void add(int id)
{
    if(++num[a[id]]==1) res++;
}

void del(int id)
{
    if(--num[a[id]]==0) res--;
}

void mo_dui()
{
    int l=1,r=0,k=0;  //k为前缀修改数量
    for(int i=1;i<=numq;i++) {
        while(k<q[i].change) add_change(++k,l,r);
        while(k>q[i].change) del_change(k--,l,r);
        while(l<q[i].l) del(l++);
        while(l>q[i].l) add(--l);
        while(r<q[i].r) add(++r);
        while(r>q[i].r) del(r--);
        ans[q[i].id]=res;
    }
}

int n,m,x,y;
char opt[10];
int main()
{
    scanf("%d %d",&n,&m);init(n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),color[i]=a[i];
    for(int i=1;i<=m;i++) {
        scanf("%s %d %d",opt+1,&x,&y);
        if(opt[1]=='Q') q[numq+1]=query(numq+1,x,y,numo),numq++;
        else o[++numo]=operate(x,color[x],y),color[x]=y;
    }
    sort(q+1,q+numq+1);
    mo_dui();
    for(int i=1;i<=numq;i++) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值