牛客练习赛66 E

牛客练习赛66 E

传送门

题意:给你一个排列,问有多少个区间满足第一个数字是次小值,最后一个值是次大值。

分析:考虑到次小值的约束,可行的右端点肯定是一个区间。即第一个小于它的值到第二个小于它的值(不包含)之间的所有位置。同理可以考虑次大值的约束。最终对于一个右端点来说,可行的左端点肯定也是一个区间值。现在考虑暴力枚举左端点,这个时候要考虑可行的右区间内,有多少个右端点可行的左区间是包含自己这个左端点的。这么暴力更新肯定是不行的。注意到一个左端点会对固定右区间的元素造成贡献,所以可以用两个端点的插入删除来代替这一个行为。并且在考虑到一个右端点时,考虑其可行区间内的区间和即可,这可以用一个BIT维护。

#include

#define PB push_back
#define MP make_pair
#define fi first
#define se second

using namespace std;

typedef pair PII;

typedef long long LL;

inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){res*=10;res+=ch-'0';ch=getchar();}

    return res*f;
}

const int N = 1000005;

int a[N], n, p[N][2], s[N][2];

int mx[N][21], mn[N][21];

int lg[N];

vector G[N];


struct fwk{
    int T[N];

    inline int lowbit(int x){return x&-x;}

    void add(int x, int v){
        while(x<=n){
            T[x]+=v;
            x+=lowbit(x);
        }
    }

    int getSum(int p){
        int res=0;
        while(p){
            res+=T[p];
            p-=lowbit(p);
        }

        return res;
    }

    int getSum(int l, int r){
        return getSum(r)-getSum(l-1);
    }

};

fwk bit;

inline int askMin(int l, int r){
    int id=lg[r-l+1];
    return min(mn[l][id],mn[r-(1<=0;--id){
           int tr=l+(1<=0;--id){
            int tr=r+(1<a[i])r+=(1<=0;--id){
            int tl=r-(1<=1&&askMax(tl,i)==a[i])r-=(1<=0;--id){
            int tl=l-(1<=1&&askMax(tl,r-1)>1]+1;

    get_pre();get_suf();

    LL ans=0;

    for(int i=1;i<=n;++i){
        if(p[i][0]==-1||p[i][1]==-1)continue;
        G[p[i][0]].PB(MP(i,1));
        G[p[i][1]+1].PB(MP(i,-1));
    }

    for(int i=1;i<=n;++i){
        for(auto it:G[i]){
            bit.add(it.fi,it.se);
        }
        if(s[i][0]==-1||s[i][1]==-1)continue;
        ans+=bit.getSum(s[i][0],s[i][1]);
    }

    printf("%lld",ans);


    return 0;
}

你可能感兴趣的:(牛客练习赛66 E)