Codeforces Round #681 (Div. 1) A. Extreme Subtraction

Extreme Subtraction

CodeForces 1442A.

题意

你有一个序列 \(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;
}
posted @ 2022-09-14 11:27  ccz9729  阅读(50)  评论(0编辑  收藏  举报