【单调队列】HDU4193

HDU4193:http://acm.hdu.edu.cn/showproblem.php?pid=4193

题目描述:有一串数列:a1,a2,a3,a4 .. an, 

它可以变形为 a2,a3,a4 .. an,a1 ;  

                      a3,a4 .. an,a1,a2 ;

                      a4 .. an,a1,a2,a3      等n种。

问有多少种变形的前缀和>=0?

例如 a1,a2,a3,a4 .. an 如果sum[1]>=0&&sum[2]>=0&&....&&sum[n]>=0则记为一种。

解法:单调队列。

首先将n个数扩展为2*n个数,然后求前缀和。sum[i]-sum[j]是j+1~i段的前缀和,

所以如果i~i+n-1段是的话,那么sum[i]-sum[i-1]>=0&&sum[i+1]-sum[i-1]>=0&&...&&sum[i+n-1]-sum[i-1]>=0,

所以min{sum[i..i+n-1]}-sum[i-1]>=0 则sum[i...i+n-1]-sum[i-1]>=0

因此,只要找出一段数中的最小值,就可以判断了。


一定要用scanf读数,否则在hdu中会超时。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
//int a[100005];
#define INF 1000000009
#define ll long long
int sum[2000005];
int qq[2000005];
int ind[2000005];
int main(){
	int n;
	while(scanf("%d",&n)!=EOF&&n){
		int topp=1,endd=1;
		sum[0]=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&sum[i]);
			sum[i+n]=sum[i];
		}
		for(int i=1;i<=2*n;i++){
			sum[i]+=sum[i-1];
		}
		int cnt=0;
		for(int i=1;i<=2*n;i++){
			if(topp!=endd&&i-ind[topp]+1>n){
				topp++;
			}
			if(topp==endd){
				qq[endd]=sum[i];
				ind[endd]=i;
				endd++;
			}
			else if(sum[i]>=qq[endd-1]){
				qq[endd]=sum[i];
				ind[endd]=i;
				endd++;
			}
			else{
				while(endd>topp){
					if(sum[i]<qq[endd-1]){
						endd--;
					}
					else
						break;
				}
				qq[endd]=sum[i];
				ind[endd]=i;
				endd++;
			}
			if(i>n){
				if(qq[topp]-sum[i-n]>=0)
					cnt++;
			}
		}
		printf("%d\n",cnt);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值