[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;
}