题意:一座山从山顶到山脚一共(N(N<=20000))棵树,山脚下有一个锯木厂(可看做在N+1号点),还能在山上修建两个锯木厂,每棵树的运输费用为该树的重量(w_i)乘上它到锯木厂的距离,只能朝山下运输树木.求最小总花费.
分析:(n^2)做法很容易想,直接枚举在那两个点修建锯木厂,然后算出此种情况下的总花费.
试着分析一下,我们已经知道了无论如何只会有三座锯木厂,锯木厂将山分成了三个小段,设三个锯木厂的位置分别为j,i,n,则1到j-1号树肯定运输到j,其余同理可得.设(f[i])表示在i点修建锯木厂的最小总花费,则有
(f[i]=sum_{k=1}^{j-1}w[k]*(sumd[k]-sumd[j])+sum_{k=j+1}^{i-1}w[k]*(sumd[k]-sumd[i])+sum_{k=i+1}^{n}w[k]*sumd[k])其中(sumd[k])表示k到山脚锯木厂的距离.
设(sumw[i])表示1到i的树木总重量,(Sum)表示将所有树木运到山脚锯木厂的花费.则有,
(f[i]=Sum-sumd[j]*sumw[j]-sumd[i]*(sumw[i]-sumw[j]))
设k<j且j比k更优,即
(Sum-sumd[j]*sumw[j]-sumd[i]*(sumw[i]-sumw[j])<Sum-sumd[k]*sumw[k]-sumd[i]*(sumw[i]-sumw[k]))
整理一下上式得到,
(frac{sumd[j]*sumw[j]-sumd[k]*sumw[k]}{sumw[j]-sumw[k]}>sumd[i])
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=20005;
int w[N],d[N],q[N],f[N];
LL sumw[N],sumd[N];
inline double count(int x,int y){return 1.0*(sumd[x]*sumw[x]-sumd[y]*sumw[y])/(sumw[x]-sumw[y]);}
int main(){
int n=read();
for(int i=1;i<=n;i++){
w[i]=read();d[i]=read();
sumw[i]=sumw[i-1]+w[i];
}
for(int i=n;i>=1;i--)sumd[i]=sumd[i+1]+d[i];
LL Sum=0;for(int i=1;i<=n;i++)Sum+=sumd[i]*w[i];
int l=1,r=1;
for(int i=1;i<=n;i++){
while(l<r&&count(q[l+1],q[l])>sumd[i])l++;
f[i]=Sum-sumd[q[l]]*sumw[q[l]]-sumd[i]*(sumw[i]-sumw[q[l]]);
while(l<r&&count(q[r],q[r-1])<count(q[r],i))r--;
q[++r]=i;
}
int ans=2e9;for(int i=1;i<=n;i++)ans=min(ans,f[i]);
printf("%d
",ans);
return 0;
}