[CEOI2004]锯木厂选址

题目

思路

\(dis[i]\)表示i到山脚的距离,\(sum[i]\)表示前i个木材的重量和,\(tot\)为不建锯木厂的代价

假设分别建在\(i,j\)位置\((i<j)\),那么有\(ans=min(tot−dis[j]∗sum[j]−dis[i]∗(sum[i]−sum[j]))\)

化简得:\(\frac{dis[j]*sum[j]-dis[k]*sum[k]}{sum[j]-sum[k]} > dis[i]\)

维护上凸壳即可

Code

#include<bits/stdc++.h>
#define N 20005 
using namespace std;
int n,w[N],d[N],dis[N],sum[N],tot;
int q[N],l=1,r=0,ans=2000000001;

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

double k(int x,int y) {return ((double)dis[x]*sum[x]-(double)dis[y]*sum[y])/(sum[x]-sum[y]);}
int main()
{
	read(n);
	for(int i=1;i<=n;++i) read(w[i]),read(d[i]);
	for(int i=1;i<=n;++i) sum[i]=sum[i-1]+w[i];
	for(int i=n;i>=1;--i) dis[i]=dis[i+1]+d[i];
	for(int i=n;i;--i) tot+=dis[i]*(sum[i]-sum[i-1]);
	for(int i=1;i<=n;++i)
	{
		while(l<r && k(q[l],q[l+1])>dis[i]) ++l;
		ans=min(ans,tot-dis[q[l]]*sum[q[l]]-dis[i]*(sum[i]-sum[q[l]]));
		while(l<r && k(i,q[r-1])<k(i,q[r])) --r;
		q[++r]=i;
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2019-10-14 21:09  擅长平地摔的艾拉酱  阅读(106)  评论(0编辑  收藏  举报
/*取消选中*/