![](https://img2020.cnblogs.com/blog/1892543/202101/1892543-20210129010408633-1304250386.png)
- 题意:有一堆石子,你每次可以选择相邻(就算两堆石子中间有很多空堆也不算)的两堆石子,使得两堆石子的个数同时(-1),你在刚开始的时候有一次交换相邻石子的机会,问你最后能否拿走所有石子.
- 题解:对于第一堆石子和最后一堆石子,它们只能靠第二堆石子和倒数第二堆石子减去才合法,所以我们由第一堆石子不断向右推和最后一堆石子不断向左推,这个过程可以用前缀和(pre)与后缀和(suf)表示.如果我们当前选择堆(<i-1,i>)的话,那么前缀和(pre[i-2])和后缀和(suf[i+1])是不会受到任何影响的,如果问题出在我们当前选择的这个堆上的话,我们就必须保证(pre[i-2])和(suf[i+1])都是合法的,同时,我们将(pre[i-2])与(a[i-1])合并后和(suf[i+1])与(a[i])合并后的值应该是合法且相等的.于是乎,我们可以枚举所有堆,然后判断不反转和反转是否合法.这里注意,如果(pre[i])不合法,我们将(pre[i]=-1),那么(i)后面的(pre)也都是不合法的,都标记为(-1),(suf)同理.
- 代码:
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define me memset
#define rep(a,b,c) for(int a=b;a<=c;++a)
#define per(a,b,c) for(int a=b;a>=c;--a)
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
int _;
int a[N];
int pre[N],suf[N];
bool check(int i){
if(pre[i-2]!=-1 && suf[i+1]!=-1 && a[i-1]>=pre[i-2] && a[i]>=suf[i+1] && a[i-1]-pre[i-2]==a[i]-suf[i+1])
return true;
return false;
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>_;
while(_--){
int n;
cin>>n;
rep(i,1,n) cin>>a[i];
pre[0]=0,suf[n+1]=0;
rep(i,1,n){
if(pre[i-1]==-1 || a[i]<pre[i-1]){
pre[i]=-1;
}else{
pre[i]=a[i]-pre[i-1];
}
}
per(i,n,1){
if(suf[i+1]==-1 || a[i]<suf[i+1]){
suf[i]=-1;
}else{
suf[i]=a[i]-suf[i+1];
}
}
bool flag=false;
rep(i,2,n){
if(check(i)) {flag=true;break;}
swap(a[i],a[i-1]);
if(check(i)) {flag=true;break;}
swap(a[i],a[i-1]);
if(pre[i-1]==-1) break; //小小的优化
}
if(flag) cout<<"YES
";
else cout<<"NO
";
}
return 0;
}