洛谷P4360 [CEOI2004]锯木厂选址(斜率优化)

传送门

 

我可能根本就没有学过斜率优化……

我们设$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 }

 

posted @ 2018-08-28 13:26  bztMinamoto  阅读(201)  评论(0编辑  收藏  举报
Live2D