【Codeforces 1474D】Cleaning | 思维、枚举

题目大意:

给出一段序列 a i a_i ai,每次可以选择相邻的两个都不为 0 0 0 a i 与 a i + 1 a_i 与 a_{i+1} aiai+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,a2a1,a3a2+a1,a4a3+a2a1,a5a4+a3a2+a1

考虑序列可以的条件:对于上述序列中的每个值都必须大于等于 0 0 0,并且最后一个数一定为 0 0 0

然后就可以考虑枚举交换哪两个数了,考虑交换相邻的两个数对当前上述的影响,因为只需要保证每个值大于等于 0 0 0,并且最后一个数等于 0 0 0,所以只需要判断一个最小值,最小值大于等于 0 0 0,并且最后一个数大于0即可。

  • 考虑交换 i 与 i + 1 i与i+1 ii+1对i之前的肯定没有影响,记一下前面的最小值。
  • 考虑交换之后对后面的影响(找一下规律):奇偶性相同的位置是 + 2 ∗ n u m [ i ] − 2 ∗ n u m [ i + 1 ] +2*num[i] - 2*num[i+1] +2num[i]2num[i+1],否则相反,然后特判一下细节就好了。

小丑竟是我自己:他们的代码都写的好短,我大概是唯一一个分类讨论的了。

【Codeforces 1474D】Cleaning | 思维、枚举_第1张图片

Code:

/*** keep hungry and calm CoolGuang!  ***/
//#pragma GCC optimize(3)
#include 
#include
#include
#include
#include
#include
#define debug(x) cout<<#x<<":"<
#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,
***/

你可能感兴趣的:(思维锻炼)