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

时间:2023-03-09 00:50:19
2018.08.28 洛谷P4360 [CEOI2004]锯木厂选址(斜率优化dp)

传送门

一道斜率优化dp入门题。

是这样的没错。。。

我们用dis[i]表示i到第三个锯木厂的距离,sum[i]表示前i棵树的总重量,w[i]为第i棵树的重量,于是发现如果令第一个锯木厂地址为i,第二个地址为j,则有

total=[∑i=1ndis[i]∗w[i]]−dis[i]∗w[i]−dis[j]∗(sum[j]−sum[i])" role="presentation" style="position: relative;">total=[∑ni=1dis[i]∗w[i]]−dis[i]∗w[i]−dis[j]∗(sum[j]−sum[i])total=[∑i=1ndis[i]∗w[i]]−dis[i]∗w[i]−dis[j]∗(sum[j]−sum[i])。

然后假设对于两个不同的i取值比较优劣的话就相当于比较斜率,于是可以用斜率优化dp。

代码:

#include<bits/stdc++.h>
#define N 30005
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,ans=2e9,tot,sum[N],dis[N],w[N],q[N],hd,tl;
inline double slope(int a,int b){return 1.0*(dis[a]*sum[a]-dis[b]*sum[b])/(sum[a]-sum[b]);}
inline int calc(int a,int b){return tot-dis[a]*sum[a]-dis[b]*(sum[b]-sum[a]);}
int main(){
    n=read();
    for(int i=1;i<=n;++i)w[i]=read(),dis[i]=read();
    for(int i=n-1;i;--i)dis[i]+=dis[i+1];
    for(int i=1;i<=n;++i)sum[i]=sum[i-1]+w[i],tot+=w[i]*dis[i];
    for(int i=1;i<=n;++i){
        while(hd<tl&&slope(q[hd],q[hd+1])>dis[i])++hd;
        ans=min(ans,calc(q[hd],i));
        while(hd<tl&&slope(q[tl-1],q[tl])<slope(q[tl],i))--tl;
        q[++tl]=i;
    }
    cout<<ans;
    return 0;
}