题意:
给你一个n项的序列,每次可以把序列的首项移动到末尾,显然一共可以构成n种序列,问一共有多少种序列满足条件:序列的前i项和都大于等于0(i:1~n)。
解题思路:
开一个2*n的数组,后面n项复制前面n项。这样,每个长度为n的区间都代表一种序列。(这也是循环序列的一般做法吧),然后,数组中的值sum[i]记录前面i项的和(1~i)。
这样,我们在考虑以k为起点的区间时,只要把sum[i]-sum[k-1]便可得到以k为起点的序列的前j项和(j=i-k+1)。
如果当前区间中的最小的sum[i]都满足sum[i]-sum[k-1]>=0,那么区间中的所有值也一定满足此条件。
问题从而转化成了如何求滚动区间中的最小值,我们不难想到单调队列的做法。
/****************************** * author :crazy_石头 * data structure: 单调队列 * created time:2013/10/31 22:18 * Pro:HDOJ 4193 * Judge Status:Accepted * Memory:19828K * Time:1078MS * PS:可以优化输入输出加速,ORZ *******************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; #define rep(i,h,m) for(int i=(h);i<=(m);i++) const int maxn=1000000+5; int sum[maxn<<1],q[maxn<<1],a[maxn<<1];//q存储下标,元素的值可以通过访问下标间接访问到; int res; int head,tail,n; inline void Enqueue(int cur)//维护递增队列; { while(head<=tail&&sum[q[tail]]>sum[cur]) tail--; q[++tail]=cur; } inline void Dequeue(int cur) { if(cur>n&&sum[q[head]]-sum[cur-n]>=0)//若最小值减去前面的sum仍旧是非负的,则开始计数; ++res;//cur>n和后面的条件不能互换,否则会变为负数越界; while(head<=tail&&cur-q[head]>=n-1) head++; } int main() { while(scanf("%d",&n)!=EOF&&n) { head=1,tail=0; res=0; memset(sum,0,sizeof(sum)); rep(i,1,n) { scanf("%d",&a[i]); a[i+n]=a[i];//往原环状序列后面复制一次该序列来处理循环序列; } rep(i,1,n<<1) { sum[i]=sum[i-1]+a[i];//维护前缀和; } rep(i,1,n<<1) { Enqueue(i); Dequeue(i); } printf("%d\n",res); } } |
* This source code was highlighted byYcdoiT. ( style: Pastie )