[CF319C] Kalila and Dimna in the Logging Industry - 斜率优化dp

Description

砍伐高度为 \(a_1,a_2,...,a_n\)\(n\) 棵树。每次他们对编号为 \(i\) 的树使用电锯,会使第 \(i\) 个树的高度降低 \(1\)。每次使用电锯后需要给它充电。充电成本取决于已完全锯掉的树木的编号(树木高度等于 \(0\) 时,我们说树木被完全锯掉 )。如果已经被完全锯掉的树的最大编号是 \(i\) ,则对电锯充电一次的成本将是 \(b_i\) 。电锯在开始时是充好电的。保证对于 \(a,b\) 的严格单调性以及 \(b_n = 0,a_1 = 1\) 。要以最低成本完全锯掉所有的树,计算这个最小代价。

Solution

只要我们锯掉第 \(n\) 棵树那么剩下的部分代价都是 \(0\),因此我们只需要考虑锯掉第 \(n\) 棵树的最小代价

\(f[i]\) 表示锯掉第 \(i\) 棵树的最小代价,则有

\[f[i]=\min_{1\le i

对于 \(j \le k\),若 \(k\) 优于 \(j\) 则有 \(f[j]+a[i]b[j] > f[k]+a[i]b[k]\),化为斜率形式为

\[\frac {(-f[k])-(-f[j])} {b[k]-b[j]} < a[i] \]

\(a[i]\) 单调递增,因此我们用单调队列维护下凸包即可

#include 
using namespace std;

#define int long long
const int N = 1000005;

int n,a[N],b[N],f[N],q[N],head,tail;

// Slope DP Utilities

int getx(int i)
{
    return b[i];
}

int gety(int i)
{
    return -f[i];
}

int getk(int i)
{
    return a[i];
}

double getslope(int j,int k)
{
    return 1.0 * (gety(k)-gety(j)) / (getx(k)-getx(j));
}

signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];

    // Push (1) into the queue
    head=tail=0;
    q[0]=1;

    for(int i=2;i<=n;i++)
    {
        while(head= getslope(q[tail],i))
        {
            --tail;
        }
        q[++tail] = i;
    }

    cout<

你可能感兴趣的:([CF319C] Kalila and Dimna in the Logging Industry - 斜率优化dp)