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_{1le i <j } (f[i]+a[i]b[j])
]
对于 (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 <bits/stdc++.h>
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<tail && getslope(q[head],q[head+1]) <= getk(i))
{
++head;
}
f[i] = f[q[head]] + a[i]*b[q[head]];
while(head<tail && getslope(q[tail-1],q[tail]) >= getslope(q[tail],i))
{
--tail;
}
q[++tail] = i;
}
cout<<f[n]<<endl;
}