1. 程式人生 > >Loj#2430. 「POI2014」沙拉餐廳 Salad Bar

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

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