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;
}