#斜率優化,單調佇列,動態規劃#bzoj 2684 洛谷 4360 鋸木場選址
阿新 • • 發佈:2018-12-16
分析
設
表示第二個鋸木場修到第i個位置
那麼狀態轉移方程是
然而這樣肯定會超時,所以必須維護一個最小的
,使
方程最優,所以說就用到了斜率優化
把
方程剖析,就得到
把已知項移出來得到
若使
更優秀,那麼
最後得到
可以發現,
是單調遞減的,也就是維護上凸殼,用單調佇列實現
程式碼
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int d[20001],w[20001],sum,n,head=1,tail=1,q[20001],ans=2147483647;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
inline signed min(int a,int b){return (a<b)?a:b;}
inline double slope(int x,int y){return 1.0*(d[x]*w[x]-d[y]*w[y])/(w[x]-w[y]);}
inline signed count(int x,int y){return sum-d[y]*w[y]-d[x]*(w[x]-w[y]);}
signed main(){
n=iut();
for (rr int i=1;i<=n;++i) w[i]=iut(),d[i]=iut();
for (rr int i=n-1;i;--i) d[i]+=d[i+1];
for (rr int i=1;i<=n;++i) sum+=d[i]*w[i],w[i]+=w[i-1];
for (rr int i=1;i<=n;++i){
while (head<tail&&slope(q[head],q[head+1])>d[i]) ++head;//找出最優的隊頭
ans=min(ans,count(i,q[head]));
while (head<tail&&slope(q[tail-1],q[tail])<slope(q[tail],i)) --tail;//移去不優的隊尾
q[++tail]=i;
}
return !printf("%d",ans);
}