【BZOJ4709】[Jsoi2011]檸檬 斜率優化+單調棧
阿新 • • 發佈:2017-10-10
iostream 這樣的 turn times == soft print ras 按順序
第 1 行:一個整數,表示 N。
第 2 .. N + 1 行:每行一個整數,第 i + 1 行表示 si。
2
2
5
2
3
//Flute 先從左端取下 4 只貝殼,它們的大小為 2, 2, 5, 2。選擇 s0 = 2,那麽這一段裏有 3 只大小為 s0 的貝殼,通過魔法可以得到 2×3^2 = 18 只檸檬。再從右端取下最後一只貝殼,通過魔法可以得到 1×3^1 = 3 只檸檬。總共可以得到 18 + 3 = 21 只檸檬。沒有比這更優的方案了。
【BZOJ4709】[Jsoi2011]檸檬
Description
Flute 很喜歡檸檬。它準備了一串用樹枝串起來的貝殼,打算用一種魔法把貝殼變成檸檬。貝殼一共有 N (1 ≤ N ≤ 100,000) 只,按順序串在樹枝上。為了方便,我們從左到右給貝殼編號 1..N。每只貝殼的大小不一定相同,貝殼 i 的大小為 si(1 ≤ si ≤10,000)。變檸檬的魔法要求,Flute 每次從樹枝一端取下一小段連續的貝殼,並選擇一種貝殼的大小 s0。如果 這一小段貝殼中 大小為 s0 的貝殼有 t 只,那麽魔法可以把這一小段貝殼變成 s0t^2 只檸檬。Flute 可以取任意多次貝殼,直到樹枝上的貝殼被全部取完。各個小段中,Flute 選擇的貝殼大小 s0 可以不同。而最終 Flute 得到的檸檬數,就是所有小段檸檬數的總和。Flute 想知道,它最多能用這一串貝殼變出多少檸檬。請你幫忙解決這個問題。Input
Output
僅一個整數,表示 Flute 最多能得到的檸檬數。Sample Input
52
2
5
2
3
Sample Output
21//Flute 先從左端取下 4 只貝殼,它們的大小為 2, 2, 5, 2。選擇 s0 = 2,那麽這一段裏有 3 只大小為 s0 的貝殼,通過魔法可以得到 2×3^2 = 18 只檸檬。再從右端取下最後一只貝殼,通過魔法可以得到 1×3^1 = 3 只檸檬。總共可以得到 18 + 3 = 21 只檸檬。沒有比這更優的方案了。
題解:大爺說他從來沒做過用單調棧優化的斜率優化,唯一的一道還是他自己出的,不過今天我也算是見過第一道這樣的題了。
首先,從兩邊進行操作可以看成只從一邊進行操作,然後我們將原序列反過來再做一遍就行了。
其次,每次施魔法時,區間的左端點和右端點一定都是相同種類的貝殼,這告訴我們應該將每種顏色放到一起處理。然後可以列出DP方程:
$f[i]=max{f[j-1]+(s[i]-s[j]+1)^2*color}$。
其中s[i]表示i這個顏色的前綴和,然後移項
$f[j-1]+(s[j]-1)^2*color=2*s[i]*color*(s[j]-1)+f[i]-s[i]*v[i]$
發現x單調遞增,y單調遞增,k也單調遞增,求的還是上凸包!所以用對於每個顏色都用一個單調棧維護即可。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #define y(_) (f[(_)-1]+(s[_]-1)*(s[_]-1)*j) #define x(_) (s[_]-1) #define k (2*s[i]*j) using namespace std; const int maxn=100010; typedef long long ll; int n,m; ll ans; int t[maxn],last[maxn],pre[maxn]; ll v[maxn],s[maxn],f[maxn],g[maxn]; vector<int> st[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { n=rd(); int i,j; for(i=1;i<=n;i++) v[i]=rd(),pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1,m=max(m,(int)v[i]); for(i=1;i<=m;i++) t[i]=-1; for(i=1;i<=n;i++) { j=v[i]; while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]]))) t[j]--,st[j].erase(st[j].end()-1); t[j]++,st[j].push_back(i); while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1]))) t[j]--,st[j].erase(st[j].end()-1); g[i]=f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j; } for(i=1;(i<<1)<=n;i++) swap(v[i],v[n-i+1]); memset(last,0,sizeof(last)); for(i=1;i<=n;i++) pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1; for(i=1;i<=m;i++) st[i].clear(),t[i]=-1; for(i=1;i<=n;i++) { j=v[i]; while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]]))) t[j]--,st[j].erase(st[j].end()-1); t[j]++,st[j].push_back(i); while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1]))) t[j]--,st[j].erase(st[j].end()-1); f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j; } ans=0; for(i=0;i<=n;i++) ans=max(ans,g[i]+f[n-i]); printf("%lld",ans); return 0; }
【BZOJ4709】[Jsoi2011]檸檬 斜率優化+單調棧