题目链接: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
-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);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。