[CEOI2004]锯木厂选址

洛咕

题意:一座山从山顶到山脚一共\(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\n",ans);
    return 0;
}

posted on 2019-06-12 08:26  PPXppx  阅读(259)  评论(0编辑  收藏  举报