[SDOI2016] 征途 - 斜率优化dp

\(n\) 段路,需要分成 \(m\) 天走,每天走的必须是完整的若干段。希望每天走的路程的方差尽量小,求最小方差 \(\times m^2\)\(n \leq 3000\)

Solution

\(f[i]\) 表示到某天走了 \(i\) 段,\(g[i]\) 表示到上一天走了 \(i\) 段,对 \(g[i]\) 建凸包,单调队列维护,转移到 \(f[i]\),转移方程为
\[ f[i]=\min_{j<i} g[j]+s^2[i]-2s[i]s[j]+s^2[j] \]
化为斜率优化形式为
\[ g[j]+s^2[j]=2s[i]\cdot s[j]-s^2[i]+f[i] \]
\(y[j]=g[j]+s^2[j], x[j]=s[j], A=2s[i], B=-s^2[i]\)

1A了,很舒服

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 3005;

int n,m,s[N],f[N],g[N],q[N],head,tail,sum;

double slope(int i,int j) {
    return 1.0*(g[i]+s[i]*s[i]-g[j]-s[j]*s[j])/(s[i]-s[j]);
}

signed main() {
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>s[i], sum+=s[i];
    for(int i=1;i<=n;i++) s[i]+=s[i-1];
    for(int i=1;i<=n;i++) f[i]=s[i]*s[i];
    for(int t=2;t<=m;t++) {
        memcpy(g,f,sizeof f);
        head=1; tail=0;
        for(int i=1;i<=n;i++) {
            while(head<tail && slope(q[head+1],q[head])<2*s[i]) ++head;
            int j=q[head];
            f[i]=g[j]+s[i]*s[i]-2*s[i]*s[j]+s[j]*s[j];
            while(head<tail && slope(q[tail-1],q[tail])>slope(q[tail],i)) --tail;
            q[++tail]=i;
        }
    }
    cout<<m*f[n]-sum*sum;
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/12464486.html