Loj#2430. 「POI2014」沙拉餐厅 Salad Bar

Loj#2430. 「POI2014」沙拉餐厅 Salad Bar

大概有将近一个月没写博客了……

代码+题解

/*
s[i]-s[L-1]>=0
s[i]>=s[L-1]

(这里本应为(s[n]-s[i-1]),但由于对于j=i+1的位置上也要符合,
把这个j代入式子得到下式: ) 
(s[n]-s[i])-(s[n]-s[R])>=0
s[R]>=s[i]

思路分析:
我们可以推导出满足条件的L,R一定满足s[L-1]<=s[i]<=s[R],其中i∈[L,R]
由此,对于一个节点i能往左推到最远的位置就是(前缀和):
设向左推第一个比他大的点的位置为x
从x到i之间最左边的最小值的位置le,
那么答案=i-le。

我们要做的就是快速求出这个点的位置。 
可以用单调栈求出每个点前面第一个比他大的数,
从单调栈每个节点开始的最小值也是可以顺带求出的。 
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
int n,a[maxn],st[maxn],f[maxn],top,ans;
//a是前缀和;st是单调栈;f[j],表示从j到当前位置的(最小值的位置); 
int _min(int x,int y){return a[f[x]]>a[f[y]]?y:x;}
int main()
{
	scanf("%d",&n);
	char c=getchar();
	while (c!='j'&&c!='p') c=getchar();a[1]=(c=='p'?1:-1);
	for (int i=2;i<=n;++i) c=getchar(),a[i]=(c=='p'?1:-1)+a[i-1];
	top=ans=0;
	for (int i=1;i<=n;++i)
	{
		st[++top]=i;f[i]=i;int id=i;
		while (top>1&&a[st[top]]>=a[st[top-1]])
		{
			int lst=st[top-1],now=st[top];
			id=_min(lst,id);//这个地方id表示的是最小值在id到i这段区间里,并非实际位置。实际位置为f[id]
			st[--top]=now;
		}
		f[st[top-1]]=f[_min(st[top-1],id)];//这就是为什么这里要套在f[ ]里面
		if (top>1||a[i]>=0) ans=max(ans,i-f[st[top-1]]);
	}
	printf("%d",ans);
	return 0;
}

相关文章: