1. 程式人生 > 其它 >【Codeforces 1474D】Cleaning | 思維、列舉

【Codeforces 1474D】Cleaning | 思維、列舉

技術標籤:Codeforces經典題目及題解整理思維鍛鍊

題目大意:

給出一段序列 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]
    ,否則相反,然後特判一下細節就好了。

小丑竟是我自己:他們的程式碼都寫的好短,我大概是唯一一個分類討論的了。

小丑竟是我自己

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, ***/