[SDOI2016] 征途

(n) 段路,需要分成 (m) 天走,每天走的必须是完整的若干段。希望每天走的路程的方差尽量小,求最小方差 ( imes 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;
}

原文地址:https://www.cnblogs.com/mollnn/p/12464486.html