【Codeforces 1474D】Cleaning | 思維、列舉
阿新 • • 發佈:2021-01-20
題目大意:
給出一段序列 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]
小丑竟是我自己:他們的程式碼都寫的好短,我大概是唯一一個分類討論的了。
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,
***/