1. 程式人生 > 實用技巧 >[線段樹] Codeforces 1436E Complicated Computations

[線段樹] Codeforces 1436E Complicated Computations

題目大意

給定一個長為 \(n(n\leq 10^5)\) 的數列 \(\{a_n\}\),其中每個數 \(a_i\) 都在 \(1\sim n\) 之中。求這個數列所有連續子數列的\(\mathrm{mex}\)值的 \(\mathrm{mex}\)

題解

想了好久,想了個巨難寫的做法,太菜了。

首先把數列中的所有數從小到大排序,相同的數按下標從小到大排序。然後我們按 \(1\sim n\) 列舉 \(x\),看 \(x\) 是否能成為子數列 \(\mathrm{mex}\) 值的 \(\mathrm{mex}\),即是否存在某個連續子陣列的 \(\mathrm{mex}\)\(x\)

,如果存在,則 \(x\) 不是子數列 \(\mathrm{mex}\) 值的 \(\mathrm{mex}\),繼續列舉 \(x\)

考慮怎麼判斷是否存在某個連續子陣列的 \(\mathrm{mex}\)\(x\),不妨把原陣列中的 \(x\) 全部刪去,原陣列就分成若干段,若其中某一段存在 \(1\sim x-1\) 中的所有數,則該段的 \(\mathrm{mex}\)\(x\)。我們可以維護兩個支援區間加的樹狀陣列 \(\mathrm{Left}\)\(\mathrm{Right}\)\(\mathrm{Left}\) 維護每一個位置上的數所處段的左端點,\(\mathrm{Right}\)

維護每一個位置上的數所處段的右端點,那麼在列舉到每一個 \(x\) 的同時,我們可以使用樹狀陣列在 \(O(\log n)\) 的時間內對每一段進行分裂和合並。然後可以使用主席樹對當次進行過分裂和合並過的段查詢 \(\mathrm{mex}\),時間複雜度 \(O(n\log n)\)

但是這樣非常難寫,其實有更好的做法。

建立一棵線段樹,我們直接從左到右掃一遍原陣列,當掃描到 \(a_m\) 時,線段樹上第 \(i\) 個位置維護 \(a_1\sim a_{m-1}\) 中等於 \(i\) 的數最後一次出現的位置,設 \(x\) 為線段樹上 \(1\sim a_{m-1}\) 中的最小值,即最後出現的位置最早的數的位置,若 \(x\)

大於上一個出現的 \(a_{m}\) 出現的位置,則說明從上一個 \(a_{m}\) 出現的位置到本次 \(a_{m}\) 出現的位置之間的這一段中,數 \(1\sim a_{m-1}\) 均有出現,即 \(a_m\) 是該段連續子陣列的 \(\mathrm{mex}\)。所以只需要一個支援單點修改和區間最小值查詢的線段樹即可。時間複雜度 \(O(n\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

const int maxn=100010;

struct SegmentTree{
    int T[maxn<<2];
    void Update(int Root,int L,int R,int pos,int val){
        if(L==R){T[Root]=val;return;}
        int mid=(L+R)>>1;
        if(pos<=mid) Update(Root<<1,L,mid,pos,val);
        else Update(Root<<1|1,mid+1,R,pos,val);
        T[Root]=min(T[Root<<1],T[Root<<1|1]);
    }

    int Query(int Root,int L,int R,int QL,int QR){
        if(QR<L || R<QL) return 1<<30;
        if(QL<=L && R<=QR) return T[Root];
        int mid=(L+R)>>1;
        return min(Query(Root<<1,L,mid,QL,QR),Query(Root<<1|1,mid+1,R,QL,QR));
    }
};
SegmentTree Tree;
int data[maxn];
bool mex[maxn];
int N;

int main(){
    Read(N);
    bool flag=true;
    for(int i=1;i<=N;++i){
        Read(data[i]);
        if(data[i]!=1) flag=false;
    }
    if(flag){printf("1\n");return 0;}
    mex[1]=true;
    for(int i=1;i<=N;++i){
        if(data[i]==1){Tree.Update(1,1,N+1,data[i],i);continue;}
        if(Tree.Query(1,1,N+1,1,data[i]-1)>Tree.Query(1,1,N+1,data[i],data[i])) mex[data[i]]=true;
        Tree.Update(1,1,N+1,data[i],i);
    }
    for(int i=2;i<=N+1;++i)
        if(Tree.Query(1,1,N+1,1,i-1)>Tree.Query(1,1,N+1,i,i)) mex[i]=true;
    for(int i=1;i<=N+2;++i)
        if(!mex[i]){printf("%d\n",i);break;}

    return 0;
}