P2629 好消息,坏消息
luogu 传送门
题目描述
给出一个 n 个数的序列,为 A0,A1,„„,An-1,循环移动 k 位之后,这个序列就变 成了 Ak,Ak+1,,,,An-1,A0,A1,,,,Ak-1。一种优秀的循环移动是,对于任意的 前 i(1<=i<=n)项和都满足不小于零。请给出这个序列优秀循环移动的个数。
题目有两种非常机智的做法。
其实挺好理解,我们用负数来吞噬它前面的数(到头后再从n开始),直到不再小于0。解释:以吞噬掉的数开头加到这个负数就会小于零,那么这样到最后剩下几个非负数就有几种方案。
可以用栈来维护。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 1000002
using namespace std;
int st[N+10],a[N],n,top;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int x=a[i];
if(x>=0)st[++top]=x;
else
{
while(x<0&&top>0)
{
x+=st[top];
top--;
}
if(x<0)
{
while(x<0&&n>i)
{
x+=a[n--];
}
if(x<0)
{
printf("0");
return 0;
}
}
st[++top]=x;//进栈!
}
}
printf("%d",top);
return 0;
}
我们处理出从1~i的前缀和最小值,和i~n前缀和最小值,以及后缀和。
枚举i,只要hm[i]>=0&&qm[i]+hs[i]>=0就是一个满足条件的 k
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 1000002
using namespace std;
int a[N],n,ans;
int qm[N];//前缀和最小值
int hm[N];//hm[i]表示以i到n的前缀和最小值(精髓)
int hs[N];//后缀和
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int tot=a[1];qm[1]=a[1];
for(int i=2;i<=n;i++)//处理前缀和最小值
{
tot+=a[i];qm[i]=min(tot,qm[i-1]);
}
hm[n]=a[n],hs[n]=a[n];
for(int i=n-1;i>=1;i--)//处理hm[]和hs[]
{
hm[i]=min(a[i],hm[i+1]+a[i]);
hs[i]=hs[i+1]+a[i];
}
for(int i=1;i<=n;i++)//枚举k的位置
{
if(hm[i]>=0&&hs[i]+qm[i]>=0) ans++;
}
printf("%d",ans);
return 0;
}