分析:
用sumw[i]记录∑ij=1w[j]∑j=1iw[j]维护前缀和
用sumd[i]记录∑i−1j=1d[j]∑j=1i−1d[j]维护前缀和
Cost[i]表示将第一个锯木厂建在i的位置时,1~i第一段的木材运到i的费用:
Cost[i]=Cost[i-1]+sumw[i-1]*d[i-1]
接着就可以用列出状态转移方程,设f[i]表示第二个锯木厂建在i时的费用
f[i]=min{ Cost[j]- sumw[j] (sumd[i]-sumd[j])- sumw[i] (sumd[n+1]-sumd[i]) }
令 y=sumw[j]*sumd[j] ,x=sumw[j] ,k=sumd[i]
维护下凸包,斜率优化乱搞即可。
下面是本蒟蒻的代码:
#include<bits/stdc++.h>
#define INF 0x7ffffffffff
using namespace std;
long long n,sumw[50005],sumd[50005],w[50005],d[50005],c[50005],q[50005],f[50005],ans=INF;
long double find(long long j,long long k){//计算斜率
return (double)(sumw[k]*sumd[k]-sumw[j]*sumd[j])/(sumw[k]-sumw[j]);
}
int main() {
scanf("%lld",&n);
for(long long i=1;i<=n;i++)scanf("%lld%lld",&w[i],&d[i]);
for(long long i=1;i<=n+1;i++) {
sumw[i]=sumw[i-1]+w[i];//求出w的前缀和
sumd[i]=sumd[i-1]+d[i-1];//求出d的前缀和
c[i]=c[i-1]+sumw[i-1]*d[i-1];//c[i]表示把第一个锯木厂设在i,第一段木材运到i的总费用
}
long long l=1,r=1;
q[1]=1;f[1]=0;
for(long long i=2;i<=n;i++) {
while(l<r&&find(q[l],q[l+1])<=sumd[i])l++;//维护队首(删除非最优决策)
f[i]=sumw[q[l]]*sumd[q[l]]-sumd[i]*sumw[q[l]]+c[n+1]-sumw[i]*(sumd[n+1]-sumd[i]);
while(l<r&&find(q[r-1],q[r])>=find(q[r],i))r--;//维护队尾(维护下凸包性质)
q[++r]=i;//入队
ans=min(ans,f[i]);
}
printf("%lld",ans);
return 0;
} 小提示:不开long long 见祖宗

京公网安备 11010502036488号