1. 程式人生 > >【BZOJ4709】檸檬(決策單調性)

【BZOJ4709】檸檬(決策單調性)

題目連結

題目描述

Flute 很喜歡檸檬。它準備了一串用樹枝串起來的貝殼,打算用一種魔法把貝殼變成檸檬。貝殼一共有 N (1 ≤ N
≤ 100,000) 只,按順序串在樹枝上。為了方便,我們從左到右給貝殼編號 1..N。每隻貝殼的大小不一定相同,
貝殼 i 的大小為 si(1 ≤ si ≤10,000)。變檸檬的魔法要求,Flute 每次從樹枝一端取下一小段連續的貝殼,並
選擇一種貝殼的大小 s0。如果 這一小段貝殼中 大小為 s0 的貝殼有 t 只,那麼魔法可以把這一小段貝殼變成 s
0t^2 只檸檬。Flute 可以取任意多次貝殼,直到樹枝上的貝殼被全部取完。各個小段中,Flute 選擇的貝殼大小 s
0 可以不同。而最終 Flute 得到的檸檬數,就是所有小段檸檬數的總和。Flute 想知道,它最多能用這一串貝殼
變出多少檸檬。請你幫忙解決這個問題。

Sol

樸素dp是 O(n2)
dp[i] 表示前i個可能產生的最大價值,直接暴力轉移

一個顯然的結論是: 把一段的首尾大小強制相同不會使答案變差

於是我們對於每一種大小分別考慮:

dp[i]=max(dp[j]+s[i](sum[i]sum[j]+1)2)

sum[i] 表示與i號貝殼大小相同的i的字首的貝殼總數

首先從前往後 dp 值一定是不降的,對於兩個可能的決策點j1,j2(j1<j2)
由於後面那一坨平方項也是單增的,並且增加得很快,那麼當

j1的轉移由於j2 時,j1在之後的決策過程中也必定會優於 j2
一個想法就是開一個棧,每次如果棧頂不優於第2個元素的時候就彈棧
但是可能會出現第3個元素能在更早的時間比棧頂優的情況,綜合考慮的話我們就是要維護一個當前位置元素比它上面的元素優時的sum[i]的最小值 遞增的一個單調棧,這樣我們每次從棧頂作為決策點就一定是最優的

其實每個決策點就是一個單調遞增的二次函式
二分出函式影象的”交點”來做最優決策

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib> #include<queue> #include<cmath> #include<set> #include<vector> using namespace std; #define Set(a,b) memset(a,b,sizeof(a)) #define POW(a) ((a)*(a)) int n; const int N=1e5+10; const int MAXN=10200; typedef long long ll; int s[N]; int *st[MAXN];int top[MAXN]; int num[MAXN]; int pool[N<<1]; ll dp[N]; int lst[MAXN],pre[N],sum[N]; inline ll calc(int p,int x,int S){return dp[p-1]+1ll*S*POW(1ll*(x-sum[p]+1));} #define INF 1e9 inline int query(int p,int q,int S) { register int l=1,r=n,pos=n+1; while(l<=r){ register int mid=l+r>>1; if(calc(p,mid,S)>=calc(q,mid,S)) r=mid-1,pos=mid; else l=mid+1; } return pos; } int main() { scanf("%d",&n); for(register int i=1;i<=n;++i) scanf("%d",&s[i]),++num[s[i]]; int h=0; for(register int i=0;i<MAXN;++i){ st[i]=&pool[h];h+=num[i]+2; } register ll ans=0; for(register int i=1;i<=n;++i){ pre[i]=lst[s[i]];lst[s[i]]=i;sum[i]=sum[pre[i]]+1; register int x=s[i]; while(top[x]>1&&query(st[x][top[x]-1],st[x][top[x]],x)<=query(st[x][top[x]],i,x)) --top[x]; st[x][++top[x]]=i; register int p,q; for(p=st[x][top[x]],q=st[x][top[x]-1];top[x]>1;--top[x],p=q,q=st[x][top[x]-1]) if(calc(p,sum[i],x)>calc(q,sum[i],x)) break; dp[i]=calc(p,sum[i],x); ans=max(ans,dp[i]); } printf("%lld\n",ans); }