Cleaning(CF1474D)

题意:
\(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;
    }
}
posted @ 2021-01-21 16:32  hachuochuo  阅读(164)  评论(0编辑  收藏  举报