【CF1443D】Extreme Subtraction 题解

原题链接

题意简介

给出一个长度为 n 的数组,现在你能对它进行如下两种操作:

  1. 前 k 个数减 1
  2. 后 k 个数减 1

问能否将每个元素减到 0 。

思路分析

乍一看是道很水的模拟题,可现实是我根本没想清楚。于是断断续续花了五六个小时才做出来。

这道题的关键在于构造凹状/不递增数列

从一个方向开始扫,每扫到一个比它的数,就意味着我们需要动用另一个方向的操作把它减小(至少减到恰好相等)。

当另一个方向不能将它减到合适,即出现为了把它减到合适不得不把其它无辜的数减到小于 0 的情形,答案就是 No 。

假如我们成功的做到了这一点,我们不难发现,此时我们已经构造出了扫的方向上的不递增数列。答案自然是 Yes 。

所以我们只需要根据前缀和思想,维护从一个方向开始算起的最小值,然后从另一个方向扫数即可。需要注意的是减的操作对还没有扫的数生效且累加,所以还需要维护一个差值 d 。

代码库

#include <cstdio>
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
#define Rep(i,a,b) for(REG int i=a;i>=b;i--)
template<typename T> inline T min(T a,T b){
    return a<b?a:b;
}
const int N=3e4+5; int n,A[N],minn[N],d,rmin;
int main(){
    REG int t; scanf("%d",&t); minn[0]=1000005; 
    while(t--){
        scanf("%d",&n); d=0; 
        rep(i,1,n) scanf("%d",A+i),minn[i]=min(minn[i-1],A[i]);
        rmin=1000005; REG bool no=0;
        Rep(i,n,1){
            //当前值: A[i]-d
            //最小值(用于判断是否递减):rmin
            //描述减去了多少:d
            if(A[i]-d<=rmin) rmin=A[i]-d;
            else if(A[i]-d-rmin<=minn[i-1]-d) d+=A[i]-d-rmin; 
            else{ no=1; break; }
        }
        printf(!no?"Yes
":"No
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Qing-LKY/p/CF1443D-solution.html