我可能根本就没有学过斜率优化……
我们设$dis[i]$表示第$i$棵树到山脚的距离,$sum[i]$表示$w$的前缀和,$tot$表示所有树运到山脚所需要的花费,$dp[i]$表示将第二个锯木厂建在$i$的最小花费
那么状态转移方程就是$$dp[i]=min\{tot-dis[j]*sum[j]-dis[i]*(sum[j]-sum[i])\}$$
然后考虑斜率优化,设$j$比$k$更优,则(一堆乱七八糟的推导之后)有$$\frac{sum[j]*dis[j]-sum[k]-dis[k]}{sum[j]-sum[k]}>dis[i]$$
那么只要考虑维护一个上凸包就可以了
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 using namespace std; 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 6 char buf[1<<21],*p1=buf,*p2=buf; 7 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} 8 inline int read(){ 9 #define num ch-'0' 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc())) 12 (ch=='-')&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return res; 17 } 18 const int N=20005; 19 int sum[N],dis[N],w[N],q[N],dp[N],n,h,t,tot,ans=0x3f3f3f3f; 20 inline double slope(int j,int k){ 21 return ((sum[j]*dis[j])-(sum[k]*dis[k]))*1.0/(sum[j]-sum[k]); 22 } 23 inline int calc(int i,int j){ 24 return tot-sum[j]*dis[j]-dis[i]*(sum[i]-sum[j]); 25 } 26 int main(){ 27 //freopen("testdata.in","r",stdin); 28 n=read(); 29 for(int i=1;i<=n;++i) w[i]=read(),dis[i]=read(); 30 for(int i=n;i;--i) dis[i]+=dis[i+1]; 31 for(int i=1;i<=n;++i) sum[i]=sum[i-1]+w[i],tot+=w[i]*dis[i]; 32 for(int i=1;i<=n;++i){ 33 while(h<t&&slope(q[h],q[h+1])>dis[i]) ++h; 34 cmin(ans,calc(i,q[h])); 35 while(h<t&&slope(q[t],q[t-1])<slope(q[t-1],i)) --t;q[++t]=i; 36 } 37 printf("%d\n",ans); 38 return 0; 39 }