bzoj1096: [ZJOI2007]仓库建设

O(n^2)的dp:

f[i]表示i~n的费用

f[i] = min(f[j+1] + c[j] + (i<=k<=j) ((x[j]-x[k])*p[k])) (i+1<=j<=n)

从后向前dp


斜率优化O(n):

s[i]表示p[i]的前缀和,s2[i]表示x[i]*p[i]的前缀和

整理一下f[i]

f[i] = min(-x[j] * s[i-1] + f[j+1]+c[j]+x[j]*s[j]-s2[j]) + s2[i-1]

变量是s[i-1]

直线k=-x[j], b=f[j+1]+c[j]+x[j]*s[j]-s2[j]

s[i-1]随i单调


#include <cstdio>
#define MAXN 1000003
int n;
long long x[MAXN], p[MAXN], c[MAXN];
long long s[MAXN], s2[MAXN], f[MAXN];
struct qtype
{
    long long k, b;
} q[MAXN];
int head, tail;
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%lld%lld%lld", &x[i], &p[i], &c[i]);
    s[0] = 0;
    for (int i = 1; i <= n; ++i)
        s[i] = s[i - 1] + p[i];
    s2[0] = 0;
    for (int i = 1; i <= n; ++i)
        s2[i] = s2[i - 1] + x[i] * p[i];
    f[n + 1] = 0;
    head = tail = 0;
    for (int i = n; i >= 1; --i)
    {
        long long k, b;
        k = -x[i];
        b = f[i + 1] + x[i] * s[i] - s2[i] + c[i];
        while (tail - head >= 2)
        {
            double x, y;
            x = (q[tail - 2].b - q[tail - 1].b) / (q[tail - 1].k - q[tail - 2].k);
            y = q[tail - 1].k * x + q[tail - 1].b;
            if (y >= k * x + b)
                --tail;
            else
                break;
        }
        q[tail].k = k, q[tail].b = b;
        ++tail;
        while (tail - head >= 2)
            if (q[head].k * s[i - 1] + q[head].b >= q[head + 1].k * s[i - 1] + q[head + 1].b)
                ++head;
            else
                break;
        f[i] = q[head].k * s[i - 1] + q[head].b + s2[i - 1];
    }
    printf("%lld\n", f[1]);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值