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

题目描述

During cleaning the coast, Alice found n piles of stones. The i-th pile has ai stones.
Piles i and i+1 are neighbouring for all 1≤i≤n−1. If pile i becomes empty, piles i−1 and i+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 t (1≤t≤104) — the number of test cases.
The first line of each test case contains the single integer n (2≤n≤2⋅105) — the number of piles.
The second line of each test case contains n integers a1,a2,…,an (1≤ai≤109) — the number of stones in each pile.
It is guaranteed that the total sum of n 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

input
5
3
1 2 1
3
1 1 2
5
2 2 2 1 3
5
2100 1900 1600 3000 1600
2
2443 2445
output
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 a=[2,2,2,3,1].
In the fourth test case, you can apply superability to the first and the second piles, thus getting a=[1900,2100,1600,3000,1600].

题目大意

给你一个序列a[],在这个序列中,你每次可以选择序列中相邻的两个数,然后让这两个数同时减1。如果a[]中的某一项变为了0,它不会消失。
并且我们有一次交换的机会,可以交换序列中相邻的两个数。问:能否把这个序列中的数全部变为0。

题目分析

我们可以先考虑一下:如果没有交换机会,这个题该怎么做。
a[1]如果要削成0,只能是通过a[2]。用a[2]将a[1]削成0后,如果再想将a[2]削成0,就只能是通过a[3]来实现。以此类推……
同理,a[n]如果要削成0,只能是通过a[n-1]。用a[n-1]将a[n]削成0后,如果再想将a[n-1]削成0,就只能是通过a[n-2]来实现。以此类推……

因此我们可以设一个前缀数组pre[]和后缀数组suf[]。
pre[i]=a[i]-pre[i-1]; suf[i]=a[i]-suf[i+1];
这样就可以求出来a[i]削去了左边/右边的数之后的值了。
注:当pre[i]/suf[i]<0时,说明用a[i]无法削去其左边或者右边的数,那么再往后的削除也都无法正常进行了,那么我可以将pre[i]/suf[i]<0的位置的值置为正无穷。

然后我们再考虑一下什么情况下,能够成功的将整个序列削为0。

当序列中出现两个相邻的数a[i]和a[i+1],其pre[i]和suf[i+1]相等时,可以将整个序列削成0。因为当出现pre[i]==suf[i+1]时,说明a[i]削去了左边的所有数后剩下的值等于a[i+1]削去了右边所有数之和剩下的值。相当于序列中只有两个相等的数,一定是可以将整个序列削为0的。

然后我们再来看看需要交换的情况。
假设我们当前需要交换a[i]和a[i+1]这一对数。交换之后我们只需要查看交换后是否能使pre[i]和suf[i+1]相等即可。 如果交换后两数相等了,则说明当前是有效交换。

因此我们先要求出交换后的pre[i]和suf[i+1]的值。首先我们知道:交换a[i]和a[i+1]这一对数,是不能够对pre[i-1]之前的数和suf[i+2]之后的数产生影响的。那么交换后的pre[i]=交换前的a[i+1]-pre[i-1];同理交换后的suf[i+1]=交换前的a[i]-suf[i+2];而此次交换能够获得正解的前提条件是a[i+1]>=pre[i-1]&&a[i]>=suf[i+2]

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#define LL long long
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int N=2e5+5,INF=0x3f3f3f3f;
int a[N];
int pre[N],suf[N];
bool check(int n)
{
	for(int i=1;i<n;i++)
		if(pre[i]==suf[i+1]) return true;		//查看是否存在合法解,是合法解则返回true
		else {							//交换当前的一组数,查看通过交换能否得到合法解
			int x=a[i],y=a[i+1];
			if(x>=suf[i+2]&&y>=pre[i-1])
			{
				x-=suf[i+2],y-=pre[i-1];
				if(x==y) return true;		//如果交换后能够得到合法解返回true
			}
		}
	return false;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		for(int i=0;i<n+10;i++) pre[i]=suf[i]=0;
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)					//求出pre[]序列的值
			if(a[i]>=pre[i-1]) pre[i]=a[i]-pre[i-1];
			else pre[i]=INF/2;
		
		for(int i=n;i;i--)						//求出suf[]序列的值
			if(a[i]>=suf[i+1]) suf[i]=a[i]-suf[i+1];
			else suf[i]=INF;
		
		if(check(n)) puts("YES");
		else puts("NO");	 
	}
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值