样例输入:
7
2
1 0
4
2 -1 -1 0
4
1 -4 3 0
4
1 -1 1 -1
5
1 2 3 4 -10
7
2 -1 1 -2 0 0 0
1
0
样例输入:
No
Yes
No
No
Yes
Yes
Yes
题意:一开始给定一个n代表序列的长度,一开始序列中所有的数都为0,且有一个指针指向第一个元素,我们可以进行下面两种操作:
1.如果指针不是指向序列中最后一个数,那么我们可以把指针当前指向的数+1,然后将指针指向后一个数
2.如果指针不是指向序列中第一个数,那么就将指针当前指向的数-1,然后将指针指向前一个数
有多组询问,每次询问给定n个数,问我们能不能将原始全为0的序列经过若干次上述两种操作后变为给定的n个数,且操作完成后指针应指向第一个数,如果能就输出Yes,否则输出No
分析:经过模拟我们发现指针从 i 移动到 j ( i < j ) 就相当于把第i个位置加1而把第j个位置-1,但是有个限制条件就是当前移动操作的起点必须要在上一次移动操作的终点左边,这个限制条件就相当于我们的前缀和必须要是一个大于等于0的数,当然这是建立在我们每次操作都是任选的前提下,但是由于第一次操作一定是从起点1开始的,所以我们有必要先解决掉这个不能任选的前提,怎么样才能导致我们下次选择起点和终点是任意的呢?因为我们下次选择的起点一定是当前终点的左边的一个点,所以我们应该让第一次选择的终点尽可能地大,所以我们直接选择最右边的负数作为第一次操作的终点,这样下一次起点的选择就可以任意了,每进行一次操作的影响是什么呢?就是令起点所在的数+1,令终点所在的数-1即可。之后直接判断前缀和是否大于等于0即可,需要注意的一点是所有数的和必须要是0.
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
const int N=2e5+110;
int a[N];
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
long long sum=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum+=a[i];
if(sum)
{
puts("No");
continue;
}
a[1]--;
for(int i=n;i>=1;i--)
{
if(a[i]<0)
{
a[i]++;
break;
}
}
sum=0;
bool flag=true;
for(int i=1;i<=n;i++)
{
sum+=a[i];
if(sum<0)
{
flag=false;
break;
}
}
if(flag) puts("Yes");
else puts("No");
}
return 0;
}