题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4193
题目大意:给定一个由n个整数组成的整数序列,可以滚动,滚动的意思就是前面k个数放到序列末尾去。问有几种滚动方法使得前面任意个数的和>=0.
解题思路:刚拿到题目有暴力求解的冲动,但看到n的范围是1-100万,瞬间就腌了,但隐约觉得这样的题目会有一些性质,这个性质找出来这题目就容易解决。这种滚动序列的题目,一般是复制一份序列到末尾,这题我也这么做了。然后找啊找啊找,发现问题可以转换成判断长度为n的序列中,前面若干个数的和的最小值是否大等于0,如果是大等于0,那它符合条件。
那现在就好办了,用个单调队列维护一个单调区间,使得区间内的数(其实是前面数的和)递增,然后判断和区间首的差值是否大等于0即可。具体实现见代码,有注释。
测试数据:
3
2 2 1
-1 1 1
-1
代码:
#include <stdio.h> #include <string.h> #define MAX 1000010 struct node{ int sum,in; }deq[MAX*2]; int ans,head,tail; int n,arr[MAX],sum[MAX*2]; int main() { int i,j,k; while (scanf("%d",&n),n) { memset(sum,0,sizeof(sum)); for (i = 1; i <= n; ++i) scanf("%d",&arr[i]); for (i = 1; i <= n; ++i) sum[i] = sum[i-1] + arr[i]; for (i = n + 1; i <= 2 * n; ++i) sum[i] = sum[i-1] + arr[i-n]; ans = 0,head = 1,tail = 0; for (i = 1; i <= 2 * n; ++i) { while (head <= tail && sum[i] <= deq[tail].sum) //让队列保持单调递增 tail--; //当前元素如果比之前的小,则肯定比之前的更优 deq[++tail].sum = sum[i]; //插入都队尾 deq[tail].in = i; //记录下标 if (i > n && deq[head].sum - sum[i-n] >= 0) //如果队列区间中最小的和大于0,则肯定符合条件 ans++; while (head <= tail && i - deq[head].in >= n - 1)//去掉不和谐的元素,也在移动区间 head++; } printf("%d\n",ans); } }