Loj#2430. 「POI2014」沙拉餐廳 Salad Bar
阿新 • • 發佈:2018-11-05
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;
}