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;
}