1474D - Cleaning(前缀+后缀+遍历查找)

1474D - Cleaning(前缀+后缀+遍历查找)

传送门

题意

n n n 堆石子,第 i i i 堆石子有 i i i 个,将第 i i i 堆石子移除后第 i − 1 i - 1 i1 i + 1 i + 1 i+1并不变成相邻。现在你每次能对两堆相邻石子个数不为空的石子堆进行两堆个数各减一的操作,问能否将所有石子全部清零?

思路

参考:D. Cleaning(思维好题+前缀)

  1. 定义 pre[i] = a[i] - pre[i - 1] ,表示从前往后消除石子到第 i i i 堆时,需要减去多少才能使其之前的所有石子堆全部清零。

    如果不能理解,我们可以先把第一堆石子看成头部,那么消除他需要第二堆的个数大于等于第一堆石子的个数,第二堆石子剩下的个数即 pre[2] = a[2] - pre[1], (pre[1] = a[1]) ;之后我们把第二个还剩余的部分看成头部,则第三堆石子剩下个数为 pre[3] = a[3] - pre[2]

    如果 pre[i] < 0,也就意味着无法将前面的石子全部消除,这时候我们把 pre[i] 的值标记为 INF / 2 ,之所以除2,是为了和 suf[i] 做出区分。

  2. 定义 suf[i] = a[i] - suf[i + 1] ,表示从后往前消除石子到第 i i i 堆时,需要减去多少才能使其之后的所有石子堆全部清零。

    如果 suf[i] < 0,也就意味着无法将后面的石子全部消除,这时候我们把 suf[i] 的值标记为 INF

  3. 能够将石子清零的充要条件是:

    存在 i ∈ ( 0 , n ) i \in (0, n) i(0,n) ,使得 pre[i] = suf[i + 1] 成立——即从前往后消除到第 i i i 堆,从后往前消除到第 i + 1 i + 1 i+1 堆的时候,剩下的两堆( pre[i], suf[i + 1] )个数相等。

    如果不相等,就尝试将 a[i], a[i + 1] 互换:

    1. pre[i] = a[i] - pre[i - 1] ,得到 pre'[i] = a[i + 1] - pre[i - 1]

    2. 同理得 suf'[i + 1] = a[i] - suf[i + 2]

    此时再比较 suf'[i + 1], pre'[i]的大小即可。(切记要判断这两个数是不是大于0)

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 2e5 + 19;
const ll mod = 1e9 + 7;

int a[N];
int pre[N];
int suf[N];
int n;

bool ok()
{
    fill(pre, pre + n + 10, 0);
    fill(suf, suf + n + 10, 0);
    
    for(int i = 0; i < n; i++)
    {
        scanf("%d", a + i);
        if(i > 0)
        {
            pre[i] = a[i] - pre[i - 1];
            if(pre[i] < 0)
            {
                pre[i] = INF / 2;
            }
        }
        else
        {
            pre[i] = a[i];
        }
    }
    
    suf[n - 1] = a[n - 1];
    for(int i = n - 2; i >= 0; i--)
    {
        suf[i] = a[i] - suf[i + 1];
        if(suf[i] < 0)
        {
            suf[i] = INF;
        }
    }
    
    for(int i = 0; i < n - 1; i++)
    {
        if(pre[i] == suf[i + 1])
            return 1;
        if(a[i] >= suf[i + 2] && a[i] - suf[i + 2] == a[i + 1] - pre[i - 1])
            return 1;
    }
    return 0;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        if(ok())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值