锯木厂选址
这是我斜率DP第一个没有一遍AC的,原因是第一遍忘开long long了。
这一题比较特殊,细心的同学一定发现了,递推式不带f。
为了方便,设d数组的后缀和为sd[i]=sd[i+1]+d[i],设k数组的前缀和为sk[i]=sk[i-1]+k[i](k[i]即是题目中的w[i])
设f[i]为第二个锯木厂选在i时的最小值,假设第一个锯木厂在j,从1~j-1运到j的和是k[p]*(sd[p]-sd[j]),p∈[1,n],从j+1~i-1运到i的和是k[p]*(sd[p]-sd[i]),p∈[j+1,i]。
从i+1~n运到第三个锯木厂的和是k[p]*sd[p],p∈[i+1,n]。设k[p]*sd[p]的和为sum。
那么整理一下此式为:f[i]=sum-sd[j]*sk[j]-sd[i]*sd[k]+sd[i]*sk[j],惊奇的发现不需要递推,可惜没有什么用。
整理成一次函数式:sd[j]*sk[j]=sd[i]*sk[j]+sum-f[i]-sd[i]*sk[i]
要让截距最大,且斜率sd[i]单调递减,那么考虑维护上凸包(不明白的一定要自己画图尝试!!)
看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=50000;
int n,d[maxn],sd[maxn],sum,sk[maxn],k[maxn];
int q[maxn],f[maxn];
int yval(int a,int b){return sd[b]*sk[b]-sd[a]*sk[a];}
int xval(int a,int b){return sk[b]-sk[a];}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&k[i],&d[i]);
sk[i]=sk[i-1]+k[i];
}
for(int i=n;i>=1;i--)
sd[i]=sd[i+1]+d[i];
for(int i=1;i<=n;i++)
sum+=k[i]*sd[i];
int l=1,r=1,ans=2147483647;
for(int i=1;i<=n;i++){
while(l<r&&yval(q[l],q[l+1])>=xval(q[l],q[l+1])*sd[i])l++;
f[i]=sd[i]*sk[q[l]]-sd[q[l]]*sk[q[l]]+sum-sd[i]*sk[i];
while(l<r&&yval(q[r-1],q[r])*xval(q[r],i)<=xval(q[r-1],q[r])*yval(q[r],i))r--;
q[++r]=i;
ans=min(ans,f[i]);
}
printf("%lld\n",ans);
return 0;
}