[WF2011] MachineWorks(李超树优化dp)

[WF2011]MachineWorks

problem

BZOJ3963

solution

来得比较快的是,直接设 d p i , j : dp_{i,j}: dpi,j: 考虑第 j j j 天换购 i i i 机器。

但是马上注意到天数是 1 e 9 1e9 1e9 级别的,而机器是 1 e 5 1e5 1e5 级别。

稍微想想,就能知道,因为每天机器只能在那一天买,而为了最大化利益,肯定不可能买了一台机器在换购下一台机器前就卖了。

所以真正有用的只有 n n n 天——离散化!!

因为机器过了那天就不能买了,所以将机器按照购买天数升序排序。

考虑 d p i : dp_i: dpi: 当前以 i i i 机器执行转型期的已知利润最大值。

有两种情况:

  • case1:就是前面一台机器都没买,用最开始的钱购买这一台机器开始赚钱,比较简单。

  • case2:考虑在换购这一台机器前是用的机器 j j j

    则有转移: d p i = max ⁡ { d p j + G j ( D i − D j − 1 ) + R j − P i } dp_i=\max\Big\{dp_j+G_j\Big(D_i-D_j-1\Big)+R_j-P_i\Big\} dpi=max{dpj+Gj(DiDj1)+RjPi}

    整理一下得到, d p i = max ⁡ { − G j ⋅ D i + d p j − G j ( D j + 1 ) + R j } − P i dp_i=\max\Big\{-G_j·D_i+dp_j-G_j(D_j+1)+R_j\Big\}-P_i dpi=max{GjDi+dpjGj(Dj+1)+Rj}Pi

然后要用 d p i dp_i dpi 去更新最后的答案, d p i dp_i dpi 是已知的利润,最后的答案就是假设这个机器不再改变。

a n s = m a x { d p i + ( T − D i ) G ( i ) + R ( i ) } ans=max\Big\{dp_i+(T-D_i)G(i)+R(i)\Big\} ans=max{dpi+(TDi)G(i)+R(i)}

当然要满足 前面赚的钱 / 最开始的钱 能够买得起这件机器,再更新,否则会出错。

比如这台机器赚的利润非常高,结果买不起,答案可能会被这台压根买不了的机器利润更新成最大值。

d p i dp_i dpi 的转移式子简直不能太熟悉了!

因为 G G G 不具有单调性,所以不能斜率优化。

就算能,李超树不香吗??只要不会超时,宁可死也要用李超不用斜率优化。

− G j -G_j Gj 当成 k k k d p j − G j ( D j + 1 ) + R j dp_j-G_j(D_j+1)+R_j dpjGj(Dj+1)+Rj 当成 b b b,每次以 D i D_i Di 查。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define int long long
int n, C, T;
int d[maxn], f[maxn];
struct Node { int D, P, R, G; }v[maxn];
struct node { int k, b; }t[maxn << 2];

#define lson now << 1
#define rson now << 1 | 1
#define mid ( ( l + r ) >> 1 )

int calc( node l, int x ) { return l.k * d[x] + l.b; }

bool cover( node Old, node New, int x ) {
    return calc( Old, x ) <= calc( New, x );
}

void insert( int now, int l, int r, node New ) {
    if( cover( t[now], New, l ) and cover( t[now], New, r ) ) {
        t[now] = New;
        return;
    }
    if( l == r ) return;
    if( cover( t[now], New, mid ) ) swap( t[now], New );
    if( cover( t[now], New, l ) ) insert( lson, l, mid, New );
    if( cover( t[now], New, r ) ) insert( rson, mid + 1, r, New );
}

int query( int now, int l, int r, int x ) {
    if( ! t[now].k and ! t[now].b ) return 0;
    if( l == r ) return calc( t[now], x );
    int ans;
    if( x <= mid ) ans = query( lson, l, mid, x );
    else ans = query( rson, mid + 1, r, x );
    return max( ans, calc( t[now], x ) );
}

signed main() {
    int Case = 0;
    while( scanf( "%lld %lld %lld", &n, &C, &T ) ) {
        if( ! n and ! C and ! T ) break;
        memset( t, 0, sizeof( t ) );
        memset( f, 0, sizeof( f ) );
        int ans = C;
        for( int i = 1, D, P, R, G;i <= n;i ++ ) {
            scanf( "%lld %lld %lld %lld", &D, &P, &R, &G );
            v[i] = { D, P, R, G };
            d[i] = D;
        }
        sort( d + 1, d + n + 1 );
        sort( v + 1, v + n + 1, []( Node x, Node y ) { return x.D < y.D; } );
        int m = unique( d + 1, d + n + 1 ) - d - 1;
        for( int i = 1;i <= n;i ++ ) v[i].D = lower_bound( d + 1, d + m + 1, v[i].D ) - d;
        for( int i = 1;i <= n;i ++ ) {
            int x = max( query( 1, 1, m, v[i].D ), C );
            if( x >= v[i].P ) {
                f[i] = x - v[i].P + v[i].R;
                ans = max( ans, f[i] + v[i].G * ( T - d[v[i].D] ) );
                insert( 1, 1, m, { v[i].G, f[i] - ( d[v[i].D] + 1 ) * v[i].G } );
            }
        }
        printf( "Case %lld: %lld\n", ++ Case, ans );
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值