[國家集訓隊]數顏色 / 維護隊列

題目描述

墨墨購買了一套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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章