Codeforces Round #681 (Div. 1) A. Extreme Subtraction
Extreme Subtraction
题意
你有一个序列 \(a\),你可以进行 \(2\) 种操作:
- 选择前 \(k\) 个数,将它们全部减 \(1\)
- 选择后 \(k\) 个数,将它们全部减 \(1\)
\(k\) 由你自己定,并且每次操作可以不同。
问是否可以把通过若干次操作整个序列变为全是 \(0\) 的序列
本题多测,有 \(t\) 组数据
\(t \le 30000\),\(\sum n \le 30000\),\(a_i \le {10}^6\)
样例 #1
样例输入 #1
4
3
1 2 1
5
11 7 9 6 8
5
1 3 1 3 1
4
5 2 1 10
样例输出 #1
YES
YES
NO
YES
SOLUTION 1
分析
两种操作都为区间操作,并且目标结果为让所有的数字都为 \(0\),那么可以对原数组的差分数组进行考虑,判断是否可以使所有的数字都相等即可,即让 \(a_i = 0(1 < i \le n)\)。第一种操作在差分数组中的含义即 a[1] --, a[i] ++
\((1 < i \le n + 1)\),第二种操作在差分数组中的含义即 a[i] ++, a[n + 1] --
。如果最终可以让\(a_i = 0(1 < i \le n)\),那么差分数组中所有的负数相加的绝对值一定是小于等于 a[1]
的。
代码
点击查看代码
#include<stdio.h>
const int N = 3E4 + 10;
int T, n, a[N];
int main() {
scanf("%d", &T);
while (T -- ) {
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", a + i);
long long now = 0;
for (int i = 2; i <= n; i ++ ) if (a[i] - a[i - 1] < 0) {
now -= (a[i] - a[i - 1]);
}
puts(now <= a[1] ? "YES" : "NO");
}
return 0;
}
SOLUTION2
分析
题意可以转化为 构造一个单调不减的数组 inc 和 一个单调不增的数组 dec,使得 a[i] = inc[i] + dec[i]
,那么我们可以贪心的构造,inc[1] = 0, dec[1] = a[1]
,考虑inc[2]
和 dec[2]
:
dec[2] <= dec[1]
dec[2] + inc[2] = a[2]
,得出dec[2] <= a[2]
inc[2] >= inc[1]
, 得出dec[2] <= a[2] - inc[1]
要贪心的让 dec[2]
尽可能的大,因此取 dec[2] = min(dec[1], a[2] - inc[1])
,最后判断此时是否有解即可。
代码
点击查看代码
#include <cstdio>
#include <algorithm>
template < typename T > inline void chkmax(T &x, T y) {x = x >= y ? x : y;}
template < typename T > inline void chkmin(T &x, T y) {x = x <= y ? x : y;}
template < typename T > inline T max(T x, T y) {return x >= y ? x : y;}
template < typename T > inline T min(T x, T y) {return x <= y ? x : y;}
constexpr int N = 3E4 + 10;
int n, a[N];
int inc[N];
int dec[N];
void solve2() {
scanf("%d", &n); //pre[0] = suf[n + 1] = 1E9;
for (int i = 1; i <= n; i ++ ) {
scanf("%d", a + i);
}
inc[1] = 0, dec[1] = a[1];
for (int i = 2; i <= n; i ++ ) {
dec[i] = min(dec[i - 1], a[i] - inc[i - 1]);
inc[i] = a[i] - dec[i];
if (inc[i] < inc[i - 1] || dec[i] > dec[i - 1] || inc[i] < 0 || dec[i] < 0) {
return puts("NO"), void();
}
}
puts("YES");
/*
如果执行将 [1, k] 的数字减去 1 这个操作,那么 a_i 执行的此时一定大于等于 a_{i+1}
因此原题可以转化为 构造一个单调不减的数组inc 和 一个单调不增的数组dec
使得 a[i] = inc[i] + dec[i]
贪心的构造,inc[1] = 0, dec[1] = a[1]
考虑inc[2] 和 dec[2]
dec[2] <= dec[1]
dec[2] + inc[2] = a[2] --> dec[2] <= a[2]
inc[2] >= inc[1] --> dec[2] <= a[2] - inc[1]
贪心的让dec[2] 尽可能的大,因此取 dec[2] = min(dec[1], a[2] - inc[1])
判断此时是否有解即可
*/
}
int main() {
int T; scanf("%d", &T);
while (T -- ) solve2();
return 0;
}
SOLUTION3
分析
考虑每个位置 \(i\),如果 \(a_i > a_{i+1}\) ,那么 \(i\) 这个位置至少要执行 \(a_{i}-a_{i+1}\) 次第一种操作,此时要考虑前缀最小值是否可以执行这么多次这个操作。 如果 \(a_i < a_{i+1}\),那么 \(i+1\) 这个位置至少要执行 \(a_{i+1}-a_{i}\) 次第二种操作,此时要考虑后缀最小值是否可以执行这么多次这个操作。
如果都满足,则可以将整个序列变为全 \(0\) 序列。
代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
template < typename T > inline void chkmax(T &x, T y) {x = x >= y ? x : y;}
template < typename T > inline void chkmin(T &x, T y) {x = x <= y ? x : y;}
constexpr int N = 3E4 + 10;
int n, a[N];
// int pre[N], suf[N];
void solve() {
scanf("%d", &n); //pre[0] = suf[n + 1] = 1E9;
for (int i = 1; i <= n; i ++ ) {
scanf("%d", a + i);
}
int mi = 1E9;
for (int i = 1; i < n; i ++ ) {
if (a[i] > a[i + 1] && mi < a[i] - a[i + 1]) {
return puts("NO"), void();
}
chkmin(mi, a[i]);
mi -= max(0, a[i] - a[i + 1]);
}
reverse(a + 1, a + 1 + n);
mi = 1E9;
for (int i = 1; i < n; i ++ ) {
if (a[i] > a[i + 1] && mi < a[i] - a[i + 1]) {
return puts("NO"), void();
}
chkmin(mi, a[i]);
mi -= max(0, a[i] - a[i + 1]);
}
puts("YES");
}
int main() {
int T; scanf("%d", &T);
while (T -- ) solve();
return 0;
}