bzoj4709 檸檬 單調棧,DP,斜率優化
阿新 • • 發佈:2019-01-13
目錄
/*
前言吐槽
我真的不知道是咋做的
不過大約就是棧的斜率優化
哪位大佬見識廣,給看看吧(乞討)
思路
s是值等於a[i]的字首和 轉移方程$f[i]=max(f[i],f[j-1]+a[i]*(s[i]-s[j]+1)*(s[i]-s[j]+1))$ 不難寫出暴力方程(by wxyww) //@baoli memset(f,-0x3f,sizeof(f)); f[0]=0; for(int i=1;i<=n;++i) { for(int j=1;j<=i;++j) { if(a[i]==a[j]) { f[i]=max(f[i],f[j-1]+a[i]*(s[i]-s[j]+1)*(s[i]-s[j]+1)); } } }
關於此題的單調性
特性1
每一段分出來的都一定是兩端相同的,顯然
特性2
他滿足斜率單調,也就是要維護凸包
ll X(int i) {return 2LL*a[i]*s[i];}
ll Y(int i) {return f[i-1]+1LL*a[i]*s[i]*s[i]-2LL*a[i]*s[i];}
特性3
如果\(j<k\)且\(f_{j-1}+a{i}*(s{i}-s{j}+1)^2 > f{k-1}+a{i}*(s{i}-s{k}+1)^2\)
顯然,f和s都是單增的
那麼對於i以後的點都是j決策大於k決策
為何?顯然(我只能這樣說),大概可以理解為\(s{i}-s{j}\)
總結思路,把他們用棧一起維護起來就是了?
錯誤
全程懵逼
程式碼
#include <cstdio> #include <vector> #define ll long long using namespace std; const int N=1e5+7; int n,a[N],s[N],vis[N],top[N]; ll f[N]; vector<int> q[N]; ll X(int i) {return 2LL*a[i]*s[i];} ll Y(int i) {return f[i-1]+1LL*a[i]*s[i]*s[i]-2LL*a[i]*s[i];} long double calc(int j,int k) {return (Y(k)-Y(j))/(long double)(X(k)-X(j));} ll dp(int i,int j) {return f[j-1]+(ll)a[i]*(s[i]-s[j]+1)*(s[i]-s[j]+1);} int main() { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]),s[i]=++vis[a[i]]; for(int i=1;i<=n;++i) if(!q[a[i]].size()) q[a[i]].push_back(0); for(int i=1;i<=n;++i) { ll p=a[i]; while(top[p]>1 && calc(q[p][top[p]],q[p][top[p]-1]) <= calc(q[p][top[p]],i)) top[p]--,q[p].pop_back(); top[p]++;q[p].push_back(i); while(top[p]>1 && calc(q[p][top[p]],q[p][top[p]-1]) <= s[i]) top[p]--,q[p].pop_back(); f[i]=dp(i,q[p][top[p]]); } printf("%lld\n", f[n]); return 0; }