[国家集训队]数颜色 / 维护队列

题目描述

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

2、 R P Col 把第P支画笔替换为颜色Col。

为了满足墨墨的要求,你知道你需要干什么了吗?

输入格式

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

输出格式

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

对于100%的数据,N≤50000,M≤50000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。


先不考虑修改颜色的操作,这个题就是个莫队的板子题。

然而这题还带修改。带修改的莫队怎么做呢?于是我赶紧去补习了一波。

对于当前询问,可以知道的是只有它之前的修改操作能够对它造成影响。所以我们可以记录下每个询问的前面有几次修改:

struct query{
    int l,r,col,pre,id;
}q[maxm];
struct change{
    int pos,val;
}c[maxm];

//main函数中
    for(register int i=1;i<=m;i++){
        cin>>op,l=read(),r=read();
        if(op=='Q'){
            q[++pos_q].l=l,q[pos_q].r=r,q[pos_q].col=l/unit+1;
            q[pos_q].pre=pos_c,q[pos_q].id=pos_q;
        }else c[++pos_c].pos=l,c[pos_c].val=r;
    }

然后我们在莫队的时候加入一个计数的变量:now记录目前做了多少次修改。然后对于当前的第i个询问,设在这个询问之前一共有pre个修改操作,那么会有三种情况:

1.now<pre。那么把now+1~pre的修改加上去即可。

2.now=pre,不需要做任何操作。

3.now>pre。那么把pre+1~now的修改删去即可。

时间复杂度为O(N^(2/3)),但我并不知道怎么来的

O3瞩目

#pragma GCC optimize(3)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define maxn 50001
#define maxm 50001
#define maxcol 1000001
using namespace std;
int n,m,col[maxn];
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}

struct query{
    int l,r,col,pre,id;
}q[maxm];
inline bool cmp(const query &a,const query &b){ return (a.col^b.col)?a.col<b.col:((a.col&1)?a.r>b.r:a.r<b.r); }
struct change{
    int pos,val;
}c[maxm];
int pos_q,pos_c,unit;
int tot,cnt[maxcol],ans[maxn];
inline void add(const int &x){ tot+=(++cnt[col[x]]==1); }
inline void del(const int &x){ tot-=(--cnt[col[x]]==0); }
inline void Change(const int &now,const int &i){
    if(q[i].l<=c[now].pos&&c[now].pos<=q[i].r){
        tot-=(--cnt[col[c[now].pos]]==0);
        tot+=(++cnt[c[now].val]==1);
    }
    swap(c[now].val,col[c[now].pos]);//用swap就可以把添加和删除询问压在一起
}
inline void MoQueue(){
    sort(q+1,q+1+pos_q,cmp);
    int l=1,r=0,now=0;
    for(register int i=1;i<=pos_q;i++){
        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--);
        while(now<q[i].pre) Change(++now,i);
        while(now>q[i].pre) Change(now--,i);
        ans[q[i].id]=tot;
    }
}

int main(){
    n=read(),m=read(),unit=sqrt(n);
    for(register int i=1;i<=n;i++) col[i]=read();
    char op; int l,r;
    for(register int i=1;i<=m;i++){
        cin>>op,l=read(),r=read();
        if(op=='Q'){
            q[++pos_q].l=l,q[pos_q].r=r,q[pos_q].col=l/unit+1;
            q[pos_q].pre=pos_c,q[pos_q].id=pos_q;
        }else c[++pos_c].pos=l,c[pos_c].val=r;
    }
    MoQueue();
    for(register int i=1;i<=pos_q;i++) printf("%d\n",ans[i]);
    return 0;
}
posted @ 2019-06-22 15:41  修电缆的建筑工  阅读(164)  评论(0编辑  收藏  举报