BZOJ 4518: [Sdoi2016]征途

Description

Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。

Input

第一行两个数 n、m。
第二行 n 个数,表示 n 段路的长度

Output

一个数,最小方差乘以 m^2 后的值

Sample Input

5 2

1 2 5 8 6

Sample Output

36

HINT

1≤n≤3000,保证从 S 到 T 的总路程不超过 30000

分析

=====m2×i=1m(aiSm)2mm2×i=1ma2i+(Sm)22aiSmmm2×(i=1ma2im+i=1mS2m32i=1maiSm2)m2×(i=1ma2im+S2m22×S2m2)m2×(i=1ma2imS2m2)m×i=1ma2iS2

因为 m 是个常数, S2 是个常数,所以只要最小化

i=1ma2i

即可

f[j][i] 表示前 i 段路,分成 j 天的最优方案对应上式的值,则有

f[j][i]=mink=1j1{f[j1][k]+(s[i]s[k])2}

直接以这个式子进行划分DP,状态数为 O(nm) ,时间复杂度为 O(nm2) ,预计得分 60 分。

尝试进行优化。首先,二维的状态存储,显然第一维是可以滚动的,设 g(i)=f[j1][i] 。考虑 k 的两个取值 k=a k=b a>b ),若 a b 优,则有

g(a)+(sisa)2g(a)+s2i+s2a2sisag(a)+s2a2sisag(a)g(b)+s2as2bg(a)g(b)+s2as2b(g(a)+s2a)(g(b)+s2b)sasb<g(b)+(sisb)2<g(b)+s2i+s2b2sisb<g(b)+s2b2sisb<2sisa2sisb<2si(sasb)<2si

然后就没了

代码

#include <bits/stdc++.h>

#define sqr(x) (1ll * (x) * (x))

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

const int N = 3050;

int n,m;
int a[N],s[N];
long long f[N],g[N];

double slope(int a,int b)
{
    return (double)(g[a] - g[b] + sqr(s[a]) - sqr(s[b])) / (double)(s[a] - s[b]);
}

int main()
{
    int n = read(), m = read();
    for (int i = 1; i <= n; i++)
        a[i] = read();
    for (int i = 1; i <= n; i++)
        s[i] = s[i - 1] + a[i];
    for (int i = 1; i <= n; i++)
        f[i] = INT_MAX;
    for (int j = 1; j <= m; j++)
    {
        memcpy(g, f, sizeof(f));
        memset(f, 0, sizeof(f));
        static int q[N];
        int l = 0, r = -1;
        q[++r] = 0;
        for (int i = 1; i <= n; i++)
        {
            while (l < r && slope(q[l + 1], q[l]) < 2 * s[i])
                l++;
            int t = q[l];
            f[i] = g[t] + sqr(s[i] - s[t]);
            while (l < r && slope(q[r], q[r - 1]) > slope(q[r], i))
                r--;
            q[++r] = i;
        }
    }
    printf("%d\n",(int)(f[n] * m - sqr(s[n])));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值