Codeforces Round 800 (Div. 1) A. Directional Increase (贪心之结论题之根本想不出来)

我们有一个长度为 n n n 的数组。最初,每个元素都等于 0 0 0 ,第一个元素上有一个指针。

我们可以按任意顺序执行以下两种操作任意次数(可能为零):

  • 如果指针不在最后一个元素上,则将指针当前所在的元素增加 1 1 1 。然后将其移动到下一个元素上。
  • 如果指针不在第一个元素上,则将指针当前所在的元素减少 1 1 1 。然后移动到上一个元素。

但还有一条额外的规则。完成后,指针必须在第一个元素上。

给你一个数组 a a a 。请判断经过一些操作后是否可以得到 a a a

输入
第一行包含一个整数 t ( 1 ≤ t ≤ 1000 ) t (1≤t≤1000) t(1t1000) 。 - 测试用例的数量。测试用例说明如下。

每个测试用例的第一行包含一个整数 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n (1≤n≤2⋅10^5) n(1n2105) - 数组 a a a 的大小。

每个测试用例的第二行包含 n n n 个整数 a 1 , a 2 , … , a n ( − 1 0 9 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_n ( −10^9≤a_i≤10^9 ) a1,a2,,an(109ai109) - 数组元素。

保证所有测试用例中 n n n 的总和不超过 2 ⋅ 1 0 5 2⋅10^5 2105

输出
对于每个测试用例,如果经过某些操作后可以获得 a a a ,则打印 “Yes”(不带引号),否则打印 “No”(不带引号)

您可以在任何情况下输出 "Yes "和 “No”(例如,字符串 “yEs”、"yes "和 "Yes "将被视为肯定回答)。


对于这道题,多加思考多少能够想出来这道题的判断方式是和每一个数的操作一二次数是有关系的,但是这道题的终极解题结论还是很难推出来的。

假设我们给出 b i b_i bi 为第 i i i 个元素进行操作 1 1 1 的次数, c i c_i ci 为第 i i i 个元素进行操作 2 2 2 的次数。
由于最终指针一定要回到第一个元素,所以 c i = b i − 1 c_i = b_{i-1} ci=bi1,并且数组的和必须是 0 0 0

对于同一个数的指针的移动,向右移动了 b i b_i bi 次,向左移动了 c i c_i ci 次,那么容易想到一定有 a i = b i − c i a_i = b_i - c_i ai=bici

那么联立两式可以得到 a i = b i − b i − 1 a_i = b_i - b_{i-1} ai=bibi1,并且我们知道 a 1 = b 1 a_1 = b_1 a1=b1,那么我们可以求得 b i b_i bi

求得b之后我们可以做什么?
我们已经知道了指针所有的向右移动的情况,那么我们可以说,如果 b i = 0 b_i = 0 bi=0,那么 b i b_i bi 之后的所有 b b b 都应该等于 0 0 0 才能够符合情况,并且 b i b_i bi 一定满足大于等于 0 0 0

那么我们可以以此为依据进行判断。


CODE:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
#define int long long

int a[N];
int b[N];

void solve(){
    int n;cin >> n;

    for(int i = 1;i <= n;i++){
        cin >> a[i];
        b[i] = b[i-1] + a[i];
    }

    if(b[n] != 0){
        cout << "NO\n";
        return;
    }

    for(int i = 1;i <= n;i++){
        if(b[i] < 0){
            cout << "NO\n";
            return;
        }
    }

    bool vis0 = 0;
    for(int i = 1;i <= n;i++){
        if(vis0 && b[i] != 0){
            cout << "NO\n";
            return;
        }
        if(b[i] == 0)vis0 = 1;
    }
    cout << "YES\n";
}

signed main(){
    int T;cin >> T;
    while(T--){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值