[线段树] 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;
}
posted @ 2020-11-16 13:45  AE酱  阅读(133)  评论(0编辑  收藏  举报