【CF1443D】Extreme Subtraction 题解 题意简介 思路分析 代码库
给出一个长度为 n 的数组,现在你能对它进行如下两种操作:
- 前 k 个数减 1
- 后 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;
}