CF1442A 极限减法

1 CF1442A 极限减法

2 题目描述

给出一个由 \(𝑛\) 个正整数组成的数组 \(𝑎\)
对它做任意多次如下操作:选择任意整数 \(k\) 满足 \(1≤𝑘≤𝑛\) 做下面的某一种操作:

  • 数组的前 \(k\) 个元素减去 \(1\)
  • 数组的后 \(k\) 个元素减去 \(1\)

例如,如果 \(𝑛=5\), \(𝑎=[3,2,2,1,4]\),则可以对它做下列操作之一(下面没有列出所有可能的操作):

  • 从数组的前两个元素递减。本次操作后 \(𝑎=[2,1,2,1,4]\)
  • 从数组的最后三个元素递减。本次操作后 \(𝑎=[3,2,1,0,3]\)
  • 从数组的前五个元素递减。本次操作后 \(𝑎=[2,1,1,0,3]\)

确定是否可以通过一定的操作运算使数组的所有元素都等于零。

3 题解

我们可以把问题转化一下:把从给出的数组通过减少达到 \(0\) 这一操作变成将一个全 \(0\) 的数组通过增加达到给出的数组。

我们假设第 \(i\) 个数 \(a_i\) 是由 \(x_i\) 次左边增加与 \(y_i\) 次右边增加构成的,那么 \(x_i + y_i = a_i\)。此时,我们可以看出两个性质:\(1. \forall i \in [2, n], x_i \leq x_{i-1} \\ 2.\forall i \in [1, n-1], y_i \le y_{i+1}\)

这两个性质的解释十分容易:如果一次左边增加的操作使第 \(i\) 个数增加了 \(1\),那么第 \(i-1\) 个数肯定也增加了 \(1\),但第 \(i+1\) 个数不一定增加了 \(1\),右边增加的操作同理。

那么什么时候我们构造不出来呢?我们发现:当我们在某个位置计算出来左边增加的最大次数为负数时,我们无论如何也无法找到可以构造出 \(a\) 数组的操作。我们可以通过上述的两个式子,推出左边增加的最大次数:\(max\_x_i = min(max\_x_{i-1}, a_i - min\_y_i)\),这是因为 \(y_i\) 的值为最小时,\(x_i\) 的值为最大,但是 \(x_i\) 的值不能超过 \(x_{i-1}\),所以要跟 \(x_{i-1}\)\(min\)。而我们观察性质 \(2\),发现: \(y_i\) 的最小值恰好为 \(y_{i-1}\),所以该式子就变为了:\(max\_x_i = min(max\_x_{i-1}, a_i - min\_y_{i-1})\)。而 \(x_{i-1}\)\(y_{i-1}\) 在上一轮中均以算出,且上一轮的 \(x_{i-1}\) 就已经是最大,\(y_{i-1}\) 已经是最小,所以我们就可以计算出每个位置左边增加的最大次数。(\(PS:y_i = a_i - max\_x_i\)

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 3e4+10;
int T, n, flag;
int a[N], x[N], y[N];
int main()
{
    scanf("%d", &T);
		while (T--)
		{
		    flag = 0;
			  scanf("%d", &n);
			  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
			  x[1] = a[1];
			  for (int i = 2; i <= n; i++)
			  {
				    x[i] = min(x[i-1], a[i] - y[i-1]);
				    if (x[i] < 0)
				    {
					      puts("NO");
					      flag = 1;
					      break;
				    }
				    y[i] = a[i] - x[i];
			  }
			  if (!flag) puts("YES");
		}
		return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-05 15:27  David24  阅读(113)  评论(0编辑  收藏  举报