1. 程式人生 > >Codeforces Round #466 (Div. 2) F. Machine Learning(帶修改莫隊)

Codeforces Round #466 (Div. 2) F. Machine Learning(帶修改莫隊)

題目大意:有n個元素,q次操作,有如下兩種操作:

1、給你一個區間[L,R],要你輸出該區間的Mex值;(Mex值是區間內數字出現次數中從1開始第一個未出現的數)

2、給你p和x,將a[p]的值更改成x。

樣例解釋:

10 4
1 2 3 1 1 2 2 2 9 9
1 1 1
1 2 8
2 7 1
1 2 8
2
3
2

對於第一次的詢問操作,[1,1]區間中數字1出現了1次,所以第一個未出現的數為2

對於第二次的詢問操作,[2,8]區間中數字3出現了1次,數字1出現了2次,數字2出現了4次,所以第一個未出現的數為3

對於第一個修改操作,將a[7]=1

對於第二次的詢問操作,[2,8]區間中數字3出現了1次,數字1出現了3次,數字2出現了3次,所以第一個未出現的數為2

題目思路:有q次詢問求數字出現次數的題目很容易就想到了用莫隊演算法,但是這個題是有修改操作的,所以我們對於每次詢問操作在離線的時候多加入一個屬性 t ,t是用來記錄當前這個詢問操作是再第 t 次修改操作之後的狀態,我們在後面移動區間求答案的時候就需要多加入一個時間節點的移動,對於當前詢問,如果上一次詢問時的時間節點是大於當前時間節點的話,就需要將時間節點退回至所需要的時間節點,再將修改過的值改回原來的值(所以修改操作也要用一個結構體儲存,記錄修改前的值和修改後的值)。由於是帶修改的莫隊,所以分塊的時候不能再分為\sqrt{n}塊了,大概是分成1/3塊左右,然後在排序的時候,最後記得將時間節點也進行排序。(因為相同時間節點的詢問就可以不用對時間節點進行移動了,可以降低時間複雜度,按普通的分塊排序這題是會T到懷疑人生的)。

具體的實現方法看程式碼吧:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define FIN freopen("in.txt","r",stdin)
#define debug(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int>pii;
const int MX=1e5+7;
const int INF=0x3f3f3f3f;

int n,m;
int a[MX],pos[MX];
int num1[MX*2],num2[MX*2];
//num2[x]表示x這個數字出現了幾次,num1[x]表示出現了x次的數字有多少個
struct node{
    int l,r,t,id;
    int ans;
}q[MX];
bool cmp(node a,node b){
    if(pos[a.l]!=pos[b.l]) return a.l<b.l;
    if(pos[a.r]!=pos[b.r]) return a.r<b.r;
    return a.t<b.t;
}
struct modity{
    int p,pre,val;
}mty[MX];
vector<int>has;//本題的a的範圍是1e9,所以要進行離散化操作
int get_id(int x){
    return lower_bound(has.begin(),has.end(),x)-has.begin()+1;
}
void add(int x){
    num1[num2[x]]--;
    num2[x]++;
    num1[num2[x]]++;
}
void del(int x){
    num1[num2[x]]--;
    num2[x]--;
    num1[num2[x]]++;
}

int main(){
    //FIN;
    num1[0]=1e8;
    scanf("%d%d",&n,&m);
    int block=2000;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        has.pb(a[i]);
        pos[i]=(i-1)/block;
    }
    int nw=0,cnt1=0,cnt2=1;
    for(int i=1;i<=m;i++){
        int op;scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&q[cnt1].l,&q[cnt1].r);
            q[cnt1].id=cnt1;
            q[cnt1].t=nw;
            cnt1++;
        } else{
            scanf("%d%d",&mty[cnt2].p,&mty[cnt2].val);nw++;
            mty[cnt2].pre=a[mty[cnt2].p];
            a[mty[cnt2].p]=mty[cnt2].val;
            has.pb(mty[cnt2].val);
            cnt2++;
        }
    }
    sort(has.begin(),has.end());
    has.erase(unique(has.begin(),has.end()),has.end());
    sort(q,q+cnt1,cmp);
    for(int i=1;i<=n;i++)
        a[i]=get_id(a[i]);
    for(int i=1;i<=cnt2;i++){
        mty[i].pre=get_id(mty[i].pre);
        mty[i].val=get_id(mty[i].val);
    }
    int tmp=nw,l=1,r=0;
    for(int i=0;i<cnt1;i++){
        int res=1;
        while(r<q[i].r) add(a[++r]);
        while(l>q[i].l) add(a[--l]);
        while(r>q[i].r) del(a[r--]);
        while(l<q[i].l) del(a[l++]);
        //時間節點的移動
        while(tmp<q[i].t){
            tmp++;
            if(mty[tmp].p>=l && mty[tmp].p<=r){
                del(mty[tmp].pre);
                add(mty[tmp].val);
            }
            a[mty[tmp].p]=mty[tmp].val;
        }
        while(tmp>q[i].t){
            if(mty[tmp].p>=l && mty[tmp].p<=r){
                del(mty[tmp].val);
                add(mty[tmp].pre);
            }
            a[mty[tmp].p]=mty[tmp].pre;
            tmp--;
        }
        while(num1[res]>0) res++;
        q[q[i].id].ans=res;
    }
    for(int i=0;i<cnt1;i++)
        printf("%d\n",q[i].ans);
    return 0;
}