前缀后缀——cf1167E

想了很久没弄明白,对于边界的情况还是有问题

等题解出了再看看

然后枚举每个后缀r,找到比它小,并且在其左边的前缀l,那么删<=l,r-1的都可以

最后的二分很迷:要多考虑特殊情况:前缀跑到后缀后面去了,那么在后缀后面加一个x+1,保证前缀必定在后缀前面

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1000000 + 10;
int n,x,a[N];
int L[N],R[N];
int pre[N],suf[N];

int main() {
    for(int i=0;i<N;i++){
        L[i]=N,R[i]=0;
    }
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        L[a[i]]=min(L[a[i]],i);
        R[a[i]]=max(R[a[i]],i);
    }

    int prelen=0,suflen=x+1;
    for(int i=1;i<=x;i++){
        if(R[i]==0)
            pre[++prelen]=pre[prelen-1];
        else {
            if(L[i]>pre[prelen]) 
                pre[++prelen]=R[i]; 
            else break;
        }
    }

    suf[suflen]=n+1;
    for(int i=x;i>=1;i--){
        if(R[i]==0){
            suf[suflen-1]=suf[suflen]; --suflen;
        } else {
            if (R[i]<suf[suflen]) {
                suf[suflen-1]=L[i]; --suflen;
            } else {
                break;
            }
        }
    }
   
    LL ans=0;
    for(int i=x+1;i>=max(suflen,2);i--) {
        // use [i,x]
        int lef=0,rig=min(i-2,prelen)+1;
        while(rig-lef>1){
            int mid=(lef+rig)>>1;
            if(pre[mid]<suf[i]){
                lef=mid;
            } else {
                rig=mid;
            }
        }
        ans=ans+(lef+1);
    }
    cout<<ans<<endl;
}

 

posted on 2019-05-16 14:05  zsben  阅读(294)  评论(0编辑  收藏  举报

导航