先骂一句,福大的OJ真是。。。连%lld都不支持,因为一个%I64d和%lld的区别,本来正确的思路,正确的程序,愣是浪费了我好几个小时并且一度怀疑自己程序的正确性。唉,也怪自己,以后用不熟悉的OJ一定要先看FAQ。
题意是这样的,给定一个数列 a1 ,a2 ,…,an-1 ,an
如果 a1+a2+a3+...+an > 0,称该数列是一个positive的数列。现在给定一个数列,其中
A(0): a1 ,a2 ,…,an-1 ,an
A(1): a2 ,a3 ,…,an ,a1
…
A(n-2): an-1 ,an ,…,an-3 ,an-2
A(n-1): an ,a1 ,…,an-2 ,an-1
问在A0到A(n-1)中有多少个是positive的。
朴素的想一想,要满足positive,那么即S1,S2,S3...Sn都大于0.如果对于每一个Ai 都进行这样的判断的话,N最大为500000,肯定TLE。所以必须想线性的算法。我们发现,可以首先将这个序列复制一倍,这样先解决了an 到 a1 的问题。然后可以维护一个单调队列,记录着一个区间的最小值,在从头到尾扫描的时候,当长度够N的时候只需判断队首元素和前边N-1项的和即可。比如在判断a3,a4,a5...an,a(n+1).a(n+2)是否满足的时候,单调队列的队首元素是这个区间的最小值,只需看queue[head] - sum[2]是否大于0即可。由于每个元素仅进队一次,出队一次,所以复杂度是线性的。
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 500002 typedef long long LL; using namespace std; LL queue[MAXN<<1],sum[MAXN<<1],a[MAXN]; int main() { int i,j,k,n,ans,T; scanf("%d",&T); for(i = 1;i <= T; i++){ scanf("%d",&n); sum[0] = 0; for(j = 1;j <= n; j++){ scanf("%lld",&a[j]); sum[j] = sum[j-1] + a[j]; } for(j = n+1;j <= 2*n; j++) sum[j] = sum[j-1] + a[j-n]; int head,tail; head = tail = ans = 0; for(j = 1;j < 2*n; j++){ while(tail > head && queue[tail-1] > sum[j]) tail --; queue[tail++] = sum[j]; //维护递增 if(j >= n){ if(queue[head]-sum[j-n] > 0) ans++; if(queue[head] == sum[j+1-n]) head++; //如果队首元素恰好是这个区间前一个元素,弹出 } } printf("Case %d: %d/n",i,ans); } }