题意:
给(n)堆石头,每堆石头的石头个数为(a[i]),每次操作可以选择相邻的两堆石头(两堆石头的数量必须大于 (0) ),让这两堆的数量都减少 (1)。相邻关系的成立由最开始确定,在其中某一堆石头个数为 (0) 以后,这堆石头的左边和右边并不会变成相邻)。问在至多交换其中相邻两堆石头一次的条件下,再经过无数次上述操作后,石头总数有没有可能变成 (0)。
想法:
-
首先考虑在没有交换次序时,如何判断这些石头能不能消除。可以发现消除一定是从两端开始,比如 (a[1] 和 a[2])消除时,结果必定是 (a[1]=0,a[2]=a[2]-a[1]),以此类推,我们可以发现在消除 (a[i] 和 a[i+1]) 时,如果 $ a[i+1]<a[i] $ ,则无法再消除。由于对称,从右向左也是如此。我们可以用 (L[i]和R[i]) 来表示在第i堆石头中,从左边开始消除时,剩下(L[i])个石头;从右边开始消除时,剩下(R[i])个石头。那么只要有一个i满足 (L[i]==R[i+1]) 时,这n堆石头是可消除的。
-
接下来看可以交换相邻次序,因为是交换相邻次序,所以直接遍历这(n-1)种情况,设(a[i]和a[i+1])交换次序,那么从第一段的结论来看,只要(L[i-1])、(a[i+1])、(a[i])、(R[i+2])这组数据满足可以消除的条件即可,那么我们可以直接去暴力判断其是否满足。
-
由于上述讲到的情况中无法对 (a[1])和(a[2])、(a[n-1])和(a[n])两种角落的交换,以及n=1时的判断都没有提到,因此再特判即可。
代码:
ll L[MAXN],R[MAXN],a[MAXN],b[MAXN];
int n;
bool check()
{
int pos;
for(int i=2;i<=n;i++){
if(b[i]<b[i-1]){
return 0;
}
b[i]-=b[i-1];
}
return b[n]==0;
}
bool check2(int i)
{
ll now[10];
now[1]=L[i-1];
now[2]=a[i+1];
now[3]=a[i];
now[4]=R[i+2];
for(int j=2;j<=4;j++){
if(now[j]<now[j-1]){
return 0;
}
now[j]-=now[j-1];
}
return now[4]==0;
}
int solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
L[i]=-INF;
R[i]=-INF;
b[i]=a[i];
}
a[0]=0;
a[n+1]=0;
if(n==1){
return 0;
}
if(check())return 1;
for(int i=0;i<=n+1;i++)b[i]=a[i];
swap(b[1],b[2]);
if(check())return 1;
for(int i=0;i<=n+1;i++)b[i]=a[i];
swap(b[n],b[n-1]);
if(check())return 1;
for(int i=0;i<=n+1;i++)b[i]=a[i];
for(int i=1;i<=n;i++){
if(b[i-1]>b[i]){
break;
}else{
L[i]=b[i]-=b[i-1];
}
}
for(int i=0;i<=n+1;i++)b[i]=a[i];
for(int i=n;i>=1;i--){
if(b[i]<b[i+1]){
break;
}else{
R[i]=b[i]-=b[i+1];
}
}
for(int i=1;i<=n;i++)b[i]=a[i];
for(int i=2;i<=n-2;i++){ //L[i-1],a[i+1],a[i],R[i+2]
if(L[i-1]==-INF||R[i+2]==-INF)continue;
if(check2(i))return 1;
}
return 0;
}
int main()
{
int T;
cin>>T;
while(T--){
if(solve())cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}