BZOJ1096[ZJOI2007]仓库建设

朴素方程:
f[i]=Min(f[j]+(sum[i]sum[j])x[i](pre[i]pre[j]))+c[i]
其中, sum[i] 表示 pi 的前缀和, pre[i] 表示 pixi 的前缀和
假设这一段的物品从起点运出,得到花费为 (sum[i]sum[j])x[i] ,而起点不在0处,那么对于物品 pi 可以少运 xi 的距离,那么就应该减去 ik=j+1pixi

时间复杂度 O(N2)

考虑优化,假设 j<k 并且 k j更优
那么得到不等式

f[k]+(sum[i]sum[k]x[i](pre[i]pre[k]))f[j]+(sum[i]sum[j]x[i](pre[i]pre[j]))

化简得到

f[j]+b[j](f[k]+b[k])sum[j]sum[k]x[i]

slope(k,j)=f[j]+b[j](f[k]+b[k])sum[j]sum[k]

剩下的,就是单调队列优化,如果队头元素满足 slope(que[l+1],que[l])
那么队头元素弹出,如果队尾元素满足 slope(i,que[r])<slope(que[r],que[r1]) ,队尾元素弹出

时间复杂度 O(N)

#include <cstdio>

using namespace std;

typedef long long LL;
typedef double dl;

const int SN = 1000000 + 10;

LL f[SN], sum[SN], x[SN], n, p[SN], c[SN], pre[SN];
LL que[SN], head, tail;

void Read(LL &x) {
    LL in = 0,f = 1;char ch = getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f = -1;ch = getchar();}
    while(ch>='0' && ch<='9') {in = in*10+ch-'0'; ch = getchar();}
    x = in*f;
}

LL get_1(int k, int j) {
    return f[j]-f[k]+pre[j]-pre[k];
}

LL get_2(int k, int j) {
    return sum[j]-sum[k];
}

dl get_3(int k, int j) {
    return (double)(get_1(k,j)/(1.0*(get_2(k,j))));
}

int main() {
    Read(n);

    for(int i = 1; i <= n; i++) {
        Read(x[i]),Read(p[i]),Read(c[i]);
        sum[i] = p[i] + sum[i-1];
        pre[i] = pre[i-1] + p[i]*x[i];
    }

    for(int i = 1; i <= n; i++) {
        while(head < tail &&get_3(que[head+1], que[head]) <= x[i])  head++;
        f[i] = f[que[head]] + c[i] + (sum[i]-sum[que[head]])*x[i] - (pre[i] - pre[que[head]]);
        while(head < tail && get_3(que[tail], que[tail-1]) >= get_3(i, que[tail])) tail--;
        que[++tail] = i;
    }

    printf("%lld\n",f[n]);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值