Codeforces-1474 D. Cleaning(前缀和)

该博客讨论了一种信息技术相关的问题,涉及到清理海岸上的石堆。每堆石头可以通过特定的操作减少数量,允许进行一次相邻堆的交换。博主提出了一个算法思路,通过维护前缀和和后缀和来判断是否能将所有石头移除。对于每个测试用例,通过检查特定条件来确定是否存在解决方案。这是一个关于数值优化和算法设计的实例。
摘要由CSDN通过智能技术生成

During cleaning the coast, Alice found 𝑛 piles of stones. The 𝑖-th pile has 𝑎𝑖 stones.

Piles 𝑖 and 𝑖+1 are neighbouring for all 1≤𝑖≤𝑛−1. If pile 𝑖 becomes empty, piles 𝑖−1 and 𝑖+1 doesn’t become neighbouring.

Alice is too lazy to remove these stones, so she asked you to take this duty. She allowed you to do only the following operation:

Select two neighboring piles and, if both of them are not empty, remove one stone from each of them.
Alice understands that sometimes it’s impossible to remove all stones with the given operation, so she allowed you to use the following superability:

Before the start of cleaning, you can select two neighboring piles and swap them.
Determine, if it is possible to remove all stones using the superability not more than once.

Input
The first line contains a single integer 𝑡 (1≤𝑡≤104) — the number of test cases.

The first line of each test case contains the single integer 𝑛 (2≤𝑛≤2⋅105) — the number of piles.

The second line of each test case contains 𝑛 integers 𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎𝑖≤109) — the number of stones in each pile.

It is guaranteed that the total sum of 𝑛 over all test cases doesn’t exceed 2⋅105.

Output
For each test case, print YES or NO — is it possible to remove all stones using the superability not more than once or not.

Example
inputCopy
5
3
1 2 1
3
1 1 2
5
2 2 2 1 3
5
2100 1900 1600 3000 1600
2
2443 2445
outputCopy
YES
YES
YES
YES
NO
Note
In the first test case, you can remove all stones without using a superability: [1,2,1]→[1,1,0]→[0,0,0].

In the second test case, you can apply superability to the second and the third piles and then act like in the first testcase.

In the third test case, you can apply superability to the fourth and the fifth piles, thus getting 𝑎=[2,2,2,3,1].

In the fourth test case, you can apply superability to the first and the second piles, thus getting 𝑎=[1900,2100,1600,3000,1600].

题意:
你可以任选相邻两个非0数,使得两者都减1。
你可以选择相邻两个数交换位置,至多操作1次。
求能否使得每个数都变成0。

思路:

  • 从边界开始考虑,很明显边界的数只能被边上一个数抵消掉(因为边上只有一个数)。因此可以维护一个前缀和代表从第一个数依次抵消到了第 i i i个数后,第 i i i个数剩下的数(如果不能抵消到第 i i i个数,结果就是INF)。同理可以维护个后缀和代表从最后的数依次抵消到第 i i i个数后第 i i i个数的值。
  • 假设没有交换操作,那么就是满足 p r e [ i ] = s u f [ i + 1 ] pre[i]=suf[i+1] pre[i]=suf[i+1]即可,也就是一个前缀和等于后缀和。
  • 如果有交换操作,那也好整,就只前缀和和后缀和中间的两个数分别考虑一下,此时变成了4个数 p r e [ i ] , a [ i + 2 ] , a [ i + 1 ] , s u f [ i + 3 ] pre[i],a[i+2],a[i+1],suf[i+3] pre[i],a[i+2],a[i+1],suf[i+3],只要保证 a [ i + 2 ] ≥ p r e [ i ] , a [ i + 1 ] ≥ s u f [ i + 3 ] a[i+2]≥pre[i],a[i+1]≥suf[i+3] a[i+2]pre[i],a[i+1]suf[i+3],且 a [ i + 2 ] − p r e [ i ] = a [ i + 1 ] − s u f [ i + 1 ] a[i+2]-pre[i]=a[i+1]-suf[i+1] a[i+2]pre[i]=a[i+1]suf[i+1],就说明交换 a [ i + 1 ] , a [ i + 2 ] a[i+1],a[i+2] a[i+1],a[i+2]后可以全部消掉。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;

typedef long long ll;

const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
int pre[maxn],suf[maxn],a[maxn];
int main() {
    int T;scanf("%d",&T);
    while(T--) {
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
        suf[n + 1] = 0;
        for(int i = 1;i <= n;i++) {
            if(a[i] >= pre[i - 1]) {
                pre[i] = a[i] - pre[i - 1];
            } else {
                pre[i] = INF;
            }
        }
        for(int i = n;i >= 1;i--) {
            if(a[i] >= suf[i + 1]) {
                suf[i] = a[i] - suf[i + 1];
            } else {
                suf[i] = INF + 1e6;
            }
        }
        int flag = 0;
        for(int i = 1;i < n;i++) {
            if(pre[i] == suf[i + 1]) {
                flag = 1;break;
            } else { //i和i+1交换
                int x = a[i + 1],y = a[i];
                if(x >= pre[i - 1] && y >= suf[i + 2]) {
                    x -= pre[i - 1];y -= suf[i + 2];
                    if(x == y) {
                        flag = 1;break;
                    }
                }
            }
        }
        if(flag) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值