1. 程式人生 > >bzoj4709 檸檬 單調棧,DP,斜率優化

bzoj4709 檸檬 單調棧,DP,斜率優化

目錄

/*

前言吐槽

我真的不知道是咋做的
不過大約就是棧的斜率優化
哪位大佬見識廣,給看看吧(乞討)

思路

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}\)

的變化量比\(s{i}-s{k}\)大0

總結思路,把他們用棧一起維護起來就是了?

錯誤

全程懵逼

程式碼

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