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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xu0_zy/article/details/83338391

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

在这里插入图片描述

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

代码+题解
当然,如果思路不够清晰的话也可以选择O(nlogn)的解法。
通过其他数据结构或算法例如:RMQ,来实现。
其实真正转化成那个优美的式子以后,可以发现实际上就是造前缀和,再求每个点 i 前面第一个比它位置 x。求出来后再求 x 到 i 中的最小值的位置 L,i - L 即为一组可行解。

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

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/83338391
bar
今日推荐