Codeforces Round #696 (Div. 2) D. Cleaning(思维)

传送门


题目大意

给出一个序列,每次可以任意选择相邻的两个数,使得两个数都减去二者的最小值。现在可以最多交换两个相邻的数,问能否将整个序列全部消为0。

解题思路

假设不能交换,我们可以从前向后依次进行上述操作,设数组 p r e [ i ] = a [ i ] − p r e [ i − 1 ] , p r e [ 0 ] = 0 pre[i] = a[i] - pre[i-1],pre[0] = 0 pre[i]=a[i]pre[i1]pre[0]=0,如果到最后一步结束后 p r e [ n ] = 0 pre[n] = 0 pre[n]=0,那么成功消成 0 0 0;显然从后向前依次进行也是同理的。

若序列不能消成0代表从前向后到达的某个位置, a [ i ] < p r e [ i − 1 ] a[i] < pre[i-1] a[i]<pre[i1]。但是 [ 1 , i − 1 ] [1,i-1] [1,i1]已经都能消成 0 0 0,对答案没有影响,可以看做前面这部分最终变成了 p r e [ i − 1 ] pre[i-1] pre[i1]一个数;同理从后向前消时,序列不能消成0代表到达的某个位置 a [ i ] < s u b [ i + 1 ] a[i] < sub[i+1] a[i]<sub[i+1]。而 [ i + 1 , n ] [i+1,n] [i+1,n]已经都能消成 0 0 0无影响,可以看做后面这部分最终变成了 s u b [ i + 1 ] sub[i+1] sub[i+1]一个数。

上述分析启发我们,枚举每对 ( a [ i ] , a [ i + 1 ] ) (a[i],a[i+1]) (a[i],a[i+1])并假设交换他们的位置,那么我们只要知道 p r e [ i − 1 ] , s u b [ i + 2 ] pre[i-1],sub[i+2] pre[i1],sub[i+2],那么就变成了四个数去消,就比较简单了。预处理 p r e , s u b pre,sub pre,sub数组时,如果得到负数那么后面的结果都置为负数即可,判断时只要 p r e [ i − 1 ] , s u b [ i + 2 ] pre[i-1],sub[i+2] pre[i1],sub[i+2]有一个小于0那么就是无效的判断。

PS:注意判断 p r e [ i − 1 ] < = a [ i + 1 ]    & &    s u b [ i + 2 ] < = a [ i ] pre[i - 1] <= a[i + 1] ~~\&\& ~~ sub[i + 2] <= a[i] pre[i1]<=a[i+1]  &&  sub[i+2]<=a[i],否则可能相减都是负数恰好相等,而倒在 t e s t 2 test2 test2

#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
typedef pair<int, int> pii;
#define ENDL "\n"
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;

int a[maxn], pre[maxn], sub[maxn];

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n;
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 0; i <= n + 1; i++) pre[i] = sub[i] = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (pre[i - 1] < 0 || pre[i - 1] > a[i]) pre[i] = -1;
            else pre[i] = a[i] - pre[i - 1];
        }
        for (int i = n; i >= 1; i--) {
            if (sub[i + 1] < 0 || sub[i + 1] > a[i]) sub[i] = -1;
            else sub[i] = a[i] - sub[i + 1];
        }
        //for (int i = 1; i <= n; i++) cout << pre[i] << " " << sub[i] << endl;
        if (n == 1) {
            cout << "NO" << ENDL;
            continue;
        }
        if (!pre[n]) {
            cout << "YES" << ENDL;
            continue;
        }
        bool ok = 0;
        for (int i = 1; i < n; i++) {
            if (pre[i - 1] < 0 || sub[i + 2] < 0) continue;
            if (pre[i - 1] <= a[i + 1] && sub[i + 2] <= a[i] && a[i + 1] - pre[i - 1] == a[i] - sub[i + 2]) {
                ok = 1;
                break;
            }
        }
        if (ok) cout << "YES" << ENDL;
        else cout << "NO" << ENDL;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值