题目大意:
给出一段序列 a i a_i ai,每次可以选择相邻的两个都不为 0 0 0的 a i 与 a i + 1 a_i 与 a_{i+1} ai与ai+1,令其都 − 1 -1 −1,(这个操作可使用无限次)。在操作之前,你可以使用一次特权:交换相邻的两个数的位置(只能使用一次)。问是否可以将序列全部变为 0 0 0.
题目思路:
首先考虑 a 1 a_1 a1一定是要被消除的,并且只能被 a 2 a_2 a2消除,那么就构成了下面的消除次数:
a 1 , a 2 − a 1 , a 3 − a 2 + a 1 , a 4 − a 3 + a 2 − a 1 , a 5 − a 4 + a 3 − a 2 + a 1 … … a_1,a_2-a_1,a_3-a_2+a_1,a_4-a_3+a_2-a_1,a_5-a_4+a_3-a_2+a_1…… a1,a2−a1,a3−a2+a1,a4−a3+a2−a1,a5−a4+a3−a2+a1……
考虑序列可以的条件:对于上述序列中的每个值都必须大于等于 0 0 0,并且最后一个数一定为 0 0 0。
然后就可以考虑枚举交换哪两个数了,考虑交换相邻的两个数对当前上述的影响,因为只需要保证每个值大于等于 0 0 0,并且最后一个数等于 0 0 0,所以只需要判断一个最小值,最小值大于等于 0 0 0,并且最后一个数大于0即可。
- 考虑交换 i 与 i + 1 i与i+1 i与i+1对i之前的肯定没有影响,记一下前面的最小值。
- 考虑交换之后对后面的影响(找一下规律):奇偶性相同的位置是 + 2 ∗ n u m [ i ] − 2 ∗ n u m [ i + 1 ] +2*num[i] - 2*num[i+1] +2∗num[i]−2∗num[i+1],否则相反,然后特判一下细节就好了。
小丑竟是我自己:他们的代码都写的好短,我大概是唯一一个分类讨论的了。
Code:
/*** keep hungry and calm CoolGuang! ***/
//#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e18+7;
const ll maxn = 1e6+700;
const int M = 1e6+8;
const ll mod= 998244353;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll num[maxn];
ll pre[maxn],premi[maxn];
int work(){
for(int i=1;i<=n;i++) pre[i] = num[i] - pre[i-1];
ll a,b;///a是奇数 b是偶数
if(n&1) a = pre[n],b = pre[n-1];
else a = pre[n-1],b = pre[n];
///特判翻转n-1,n
int flag = 0;
swap(num[n-1],num[n]);
for(int i=1;i<=n;i++){
pre[i] = num[i] - pre[i-1];
if(pre[i] < 0) flag = 1;
}
if(!flag && pre[n] == 0) return 1;
///
swap(num[n-1],num[n]);
for(int i=1;i<=n;i++) pre[i] = num[i] - pre[i-1];
premi[1] = pre[1];
for(int i=2;i<=n;i++) premi[i] = min(pre[i],premi[i-1]);
for(int i=n-2;i>=1;i--){
if(pre[i]-num[i]+num[i+1]>=0){
if(i&1){
ll tempa = a-2*num[i]+2*num[i+1];
ll tempb = b-2*num[i+1]+2*num[i];
ll tempc = (n&1)?pre[n]-2*num[i]+2*num[i+1]:pre[n]-2*num[i+1]+2*num[i];
if(tempa>=0&&tempb>=0&&tempc==0&&premi[i-1]>=0) return 1;
}
else{
ll tempa = a+2*num[i]-2*num[i+1];//不同
ll tempb = b+2*num[i+1]-2*num[i];
ll tempc = (n&1)?pre[n]+2*num[i]-2*num[i+1]:pre[n]+2*num[i+1]-2*num[i];
if(tempa>=0&&tempb>=0&&tempc==0&&premi[i-1]>=0) return 1;
}
}
if(i&1) a = min(a,pre[i]*1ll);
else b = min(b,pre[i]*1ll);
}
return 0;
}
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
for(int i=1;i<=n;i++) read(num[i]);
int flag = 0;
for(int i=1;i<=n;i++){
pre[i] = num[i] - pre[i-1];
if(pre[i] < 0) flag = 1;
}
if(!flag && pre[n] == 0) printf("YES\n");
else{
int f = work();
if(f){
printf("YES\n");
continue;
}
///逆置
int l = 1,r = n;
while(l<=r){
swap(num[l],num[r]);
l++;
r--;
}
f = work();
if(f) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
/***
a1,a2,a3
a1,a2-a1,a3-a2+a1
a1,a3-a1,a2-a3+a1
a1,a2-a1,a3-a2+a1,a4-a3+a2-a1,a5-a4+a3-a2+a1
a1,a3-a1,a2-a3+a1,
***/