codeforces1167E. Range Deleting

本文探讨了一种特定数组操作f(l,r),旨在通过删除指定范围内的元素使数组变为非减顺序。文章详细介绍了如何利用前缀和后缀的概念确定有效的操作范围,以及如何计算所有可能的操作数量。

题目链接 琪亚娜世界第一可爱

给出长度为n的以一个数组,数组中的元素最大不超过x。现在你可以执行一个操作 f(l,r),将数组中所有大于等于l且小于等于r的元素都删除,现在求有多少种方案可以使执行完这个操作后的数组为非减顺序


首先因为空串被认为是符合的,所以f(1,x)是一定可行,并由此推得f(1,x-1)也是一定可行的。所以我们可以从后向前推,直到出现第一个不符合要求的f(1,min_suffix),这样以1开始的所有符合要求的操作就是f(1,min_suffix…x)。
之后我们再考虑以2开始的操作,同样f(2,x)也是一定可行,之后再向后判断,找出f(2,min_suffix)。
从3开始时我们就要判断其成立条件,也就是说f(3,x)是不一定可行的,因为{1,2}的顺序不定,所以要先判断1与2的位置关系,再找到f(3,min_suffix)
这样重复起始位置的增加,直到以n+1开始,{1,2…n}在原序列中的顺序不符合要求,所以在n+1以后包括n+1的操作就都不用再考虑了。

而在上述过程中,min_suffix是固定的,且min_suffix+1就是原序列中最长非递减后缀的起始位置上的元素值。n-1则是原序列中最长非递减前缀的结束位置上的元素值

所以现在问题转换为对于元素值i来说,求最小的cur_lim使得f(i,cur_lim)成立。首先要确保操作后没有比 i-1 大的数出现在 i-1 前面,故要删除到 最后一个 i-1 出现前的最大值 before_max[i-1] ;其次,还要至少删到min_suffix,所有取这两个数的最大值。因为i一定在非递减前缀中,所以可以用before_max[i]来替代before_max[i-1]。
再来看一下上下边界

  • 当 i==1 时
    这是我们只需要删到min_suffix而不用关心在1前的最大值
  • 当 i==n 时
    这是n不在非递减前缀中,而是这个前缀中最大值+1 。虽然它不在前缀中,但是可以有操作使结果非递减,也是唯一一个特殊的位置。最后的结果是 完整的最长非递减前缀 + 最长非递减后缀 。所以在这里我们要取 max(before_max[1],min_suffix)为cur_lim,为的是让这个前缀的第一个元素成立。

这样我们就处理完了所有出现过的元素,而对于没出现过的元素,我们并不能跳过,也要记入答案中。记这个值为val,由于val没有出现过,所以before_max没有关于val的记录,所以取上一次出现过的元素的cur_lim为参数, 取max(cur_lim,i)为cur_lim 。


#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn=1e6+5;

int N,x;
int store[maxn];
int first_pos[maxn],last_pos[maxn],max_before[maxn];
map<int,bool> appear;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int m=-1;
    cin>>N>>x;
    rep(i,1,N+1){
        cin>>store[i];
        int &num=store[i];

        if(!appear[num]){
            appear[num]=true;
            first_pos[num]=i;
        }

        last_pos[num]=i;
        m=max(m,num);
        max_before[num]=m;
    }

    first_pos[x+1]=maxn;
    int min_suffix=1,pos=maxn;
    drep(i,x,1){
        if(!first_pos[i])
            continue;
        if(last_pos[i]>pos){
            min_suffix=i;
            break;
        }

        pos=min(pos,first_pos[i]);
    }

    pos=last_pos[1];
    int cur_lim=max(min_suffix,max_before[1]);
    ll ans=x-min_suffix+1;
    rep(i,2,x+1){
        if(!first_pos[i]){
            ans+=x-max(cur_lim,i)+1;
            continue;
        }

        if(pos>first_pos[i]){
            ans+=x-max(min_suffix,max_before[1])+1;
            break;
        }

        cur_lim=max(cur_lim,max_before[i]);
        ans+=x-cur_lim+1;

        pos=max(pos,last_pos[i]);
    }

    cout<<ans<<endl;

    re 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值