hdu Non-negative Partial Sums(单调队列)

Non-negative Partial Sums

Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other)
Total Submission(s) : 6   Accepted Submission(s) : 1
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

You are given a sequence of n numbers a 0,..., a n-1. A cyclic shift by k positions (0<=k<=n-1) results in the following sequence: a ka k+1,..., a n-1, a 0, a 1,..., a k-1. How many of the n cyclic shifts satisfy the condition that the sum of the fi rst i numbers is greater
than or equal to zero for all i with 1<=i<=n?

Input

Each test case consists of two lines. The fi rst contains the number n (1<=n<=10 6), the number of integers in the sequence. The second contains n integers a 0,..., a n-1 (-1000<=a i<=1000) representing the sequence of numbers. The input will finish with a line containing 0.

Output

For each test case, print one line with the number of cyclic shifts of the given sequence which satisfy the condition stated above.

Sample Input

Sample Output

3
2
0

Source

SWERC 2011


做的第一道单调队列的题,还是记录一下
先说单调队列的实现:可以利用双端队列,每当要插入一个元素,就和队尾的元素比较,如果
<=(保证队首元素最小) 队尾的元素, 就把队尾的元素 抛掉, 然后再将改元素插入队尾;
每当要使用队首元素时,先判断队首元素的下标是否“过时”,“过时”则抛掉,然后使用。
这就是单调队列的比较直白的实现 方法。

再说这样做的意义:当遇到在动态区间中求最大或最小值问题的时候,朴素算法可以每次都从头到尾比较一遍。
但是这样做很浪费, 因为在我们每 次求上一段区间的最小值时,实际上已经比较过了下一段所求区间的n-1个元素,
所以为了能利用这些以作的工作成果, 我们需要设计一种巧妙的 方式。于是有了单调队列这种数据结构。
设想,如果自己去设计一种结构需要解决哪些问题?
1.上一段区间的最值的位置的不确定性(也可以每 次记录下最值得位置,然后再分情况讨论下一段区间的最值,
但个人感觉这样很麻烦, 没有尝试,再加上单调队列的实现是很优美的,所以完全 不用考虑存在一种更好的方法)
导致不太好利用以求的工作成果。
2.数据的不断更新需要记录那些数据过时,哪些没过时,方便确定上一段区间 所得最值属于哪一种。

再来想想单调队列是怎么解决这些问题的:
1.每次遇到<=的元素就抛掉,是因为这些元素肯定不是这一段区间的最值, 所以对求下一段区间最值 无影响。
2。新来的元素都是从队尾开始比较,保证了队列从队尾到队首的元素一定是从最新的到最老的,
这样做就解决了问题1最值位置的不确 定性。
3.每次使用队首元素时先判断是否过时,过时就抛掉,这就解决了问题2。
总的来说,单调队列的思想就是,利用队尾插入保证元素的新旧顺序,插入时只保存可能影响下一段区间的元素,
使用队首元素时先判断是否过时,删掉对求下一段最值无影响的元素。

再来说说这道题本身的一些有价值的地方:1.求一段区间的和可以利用
sum(x~y)= sum(0~y)— sum(0~x),
这样只要预先处理了sum 数组,就可以很方便的得到任意段区间的和,不用每次都从头到尾求,做了大量重复。
2.循环序列的处理方法一般都是直接将原序列复制一遍接到末尾扩大一倍。
3.问题转化为求区间内的sum()最小值,实际上是抓住了问题的 充要条件 :区间内sum()最小值能成立的话,
该区间一定满足,若不能成立,则不能满足。

#include <stdio.h>
#include <string.h>
#include <deque>
using namespace std;

struct Node{
    int a;
    int i;
}sum[2000005];
deque <struct Node> d;
int a[2000005];
int ans;
int n;

void Insert(struct Node a){
    while(!d.empty() && d.back().a >= a.a){
        d.pop_back();
    }
    d.push_back(a);;
    while(!d.empty() && a.i - d.front().i >= n){//注意这里是>=,而不是>
        d.pop_front();
    }
}

int main(){
    int i;

    for(i = 0;i < 2000005;i++){
        sum[i].i  = i;
    }
    while(scanf("%d",&n) && n){
        ans = 0;
        while(!d.empty()){
            d.pop_back();
        }
        for(i = 0;i < n;i++){
            scanf("%d",&a[i]);
            a[i+n] = a[i];
        }
        sum[0].a = a[0];
        for(i = 1;i < 2*n-1;i++){
            sum[i].a = sum[i-1].a + a[i];
        }
        for(i = 0;i < n-1;i++){
            Insert(sum[i]);
        }
        for(i = n-1;i < 2*n - 1;i++){
            Insert(sum[i]);
            if(d.front().a - sum[i-n].a >= 0)
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值