hdu 4193(单调队列)

题意:给你一个n项的序列,每次可以把序列的首项移动到末尾,显然一共可以构成 n 种序列,问一共有多少种序列满足条件:序列的前 i 项和都大于等于0(i:1~n)。

解题思路:这道题我想的复杂了,实际上单调队列的做法并不复杂。求前缀和就可以用sum[j]-sum[i-1],这个式子表示以第i的元素为首位的序列,然后以第j个元素结尾的前缀和。同一个序列的不同结尾的前缀和每次都是减sum[i-1],只有sum[j]不同,所以我们就求出sum[j]中最小的再减去sum[i-1]看是否小于0即可。也就是说,在第i个序列中,最小的前缀和都大于等于0,那么肯定是符合题意的序列。因此我们现在就是要求一个长度为n的区间里面sum[j]的最小值,也就是一个移动的固定区间求最值,用单调队列O(n)解决。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 1000005;
int n,a[maxn<<1],sum[maxn];
int head,tail,q[maxn];

void InQueue(int i)
{
	while(head < tail && sum[i] <= sum[q[tail-1]]) tail--;
	q[tail++] = i;
}

void OutQueue(int i)
{
	while(head < tail && q[head] < i - n + 1) 
		head++;
}

int main()
{
	while(scanf("%d",&n),n)
	{
		for(int i = 1; i <= n; i++)
		{
			scanf("%d",&sum[i]);
			sum[i + n] = sum[i];
		}
		for(int i = 2; i < 2*n; i++)
			sum[i] += sum[i-1];
		head = tail = 0;
		for(int i = 1; i < n; i++)
			InQueue(i);
		int ans = 0;
		for(int i = n; i < 2*n; i++)
		{
			InQueue(i);
			OutQueue(i);
			if(sum[q[head]] - sum[i-n] >= 0)
				ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(数据结构)