1474D - Cleaning(前缀+后缀+遍历查找)
题意
有 n n n 堆石子,第 i i i 堆石子有 i i i 个,将第 i i i 堆石子移除后第 i − 1 i - 1 i−1 和 i + 1 i + 1 i+1 堆并不变成相邻。现在你每次能对两堆相邻且石子个数不为空的石子堆进行两堆个数各减一的操作,问能否将所有石子全部清零?
思路
-
定义
pre[i] = a[i] - pre[i - 1]
,表示从前往后消除石子到第 i i i 堆时,需要减去多少才能使其之前的所有石子堆全部清零。如果不能理解,我们可以先把第一堆石子看成头部,那么消除他需要第二堆的个数大于等于第一堆石子的个数,第二堆石子剩下的个数即
pre[2] = a[2] - pre[1], (pre[1] = a[1])
;之后我们把第二个还剩余的部分看成头部,则第三堆石子剩下个数为pre[3] = a[3] - pre[2]
。如果
pre[i] < 0
,也就意味着无法将前面的石子全部消除,这时候我们把pre[i]
的值标记为INF / 2
,之所以除2,是为了和suf[i]
做出区分。 -
定义
suf[i] = a[i] - suf[i + 1]
,表示从后往前消除石子到第 i i i 堆时,需要减去多少才能使其之后的所有石子堆全部清零。如果
suf[i] < 0
,也就意味着无法将后面的石子全部消除,这时候我们把suf[i]
的值标记为INF
。 -
能够将石子清零的充要条件是:
存在 i ∈ ( 0 , n ) i \in (0, n) i∈(0,n) ,使得
pre[i] = suf[i + 1]
成立——即从前往后消除到第 i i i 堆,从后往前消除到第 i + 1 i + 1 i+1 堆的时候,剩下的两堆(pre[i], suf[i + 1]
)个数相等。如果不相等,就尝试将
a[i], a[i + 1]
互换:-
由
pre[i] = a[i] - pre[i - 1]
,得到pre'[i] = a[i + 1] - pre[i - 1]
; -
同理得
suf'[i + 1] = a[i] - suf[i + 2]
。
此时再比较
suf'[i + 1], pre'[i]
的大小即可。(切记要判断这两个数是不是大于0) -
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 2e5 + 19;
const ll mod = 1e9 + 7;
int a[N];
int pre[N];
int suf[N];
int n;
bool ok()
{
fill(pre, pre + n + 10, 0);
fill(suf, suf + n + 10, 0);
for(int i = 0; i < n; i++)
{
scanf("%d", a + i);
if(i > 0)
{
pre[i] = a[i] - pre[i - 1];
if(pre[i] < 0)
{
pre[i] = INF / 2;
}
}
else
{
pre[i] = a[i];
}
}
suf[n - 1] = a[n - 1];
for(int i = n - 2; i >= 0; i--)
{
suf[i] = a[i] - suf[i + 1];
if(suf[i] < 0)
{
suf[i] = INF;
}
}
for(int i = 0; i < n - 1; i++)
{
if(pre[i] == suf[i + 1])
return 1;
if(a[i] >= suf[i + 2] && a[i] - suf[i + 2] == a[i + 1] - pre[i - 1])
return 1;
}
return 0;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
if(ok())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}