分析
设
d
p
[
i
]
dp[i]
dp[i]表示第二个锯木场修到第i个位置
那么状态转移方程是
d
p
[
i
]
=
m
i
n
(
t
o
t
−
d
i
s
[
j
]
(
从
j
到
n
的
距
离
)
(
后
缀
和
)
∗
(
∑
k
=
1
j
w
[
k
]
(
前
缀
和
)
)
−
d
i
s
[
i
]
∗
(
∑
k
=
j
+
1
i
w
[
k
]
)
)
(
j
<
i
)
dp[i]=min(tot-dis[j](从j到n的距离)(后缀和)*(\sum_{k=1}^{j}w[k](前缀和))-dis[i]*(\sum_{k=j+1}^{i}w[k]))(j<i)
dp[i]=min(tot−dis[j](从j到n的距离)(后缀和)∗(k=1∑jw[k](前缀和))−dis[i]∗(k=j+1∑iw[k]))(j<i)
然而这样肯定会超时,所以必须维护一个最小的
j
j
j,使
d
p
dp
dp方程最优,所以说就用到了斜率优化
把
d
p
dp
dp方程剖析,就得到
t
o
t
−
d
i
s
[
j
]
∗
s
u
m
[
j
]
−
d
i
s
[
i
]
∗
(
s
u
m
[
i
]
−
s
u
m
[
j
]
)
tot-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])
tot−dis[j]∗sum[j]−dis[i]∗(sum[i]−sum[j])
把已知项移出来得到
t
o
t
−
d
i
s
[
i
]
∗
s
u
m
[
i
]
−
d
i
s
[
i
]
∗
s
u
m
[
j
]
−
d
i
s
[
j
]
∗
s
u
m
[
j
]
tot-dis[i]*sum[i]-dis[i]*sum[j]-dis[j]*sum[j]
tot−dis[i]∗sum[i]−dis[i]∗sum[j]−dis[j]∗sum[j]
若使
k
(
j
<
k
)
k(j<k)
k(j<k)更优秀,那么
t
o
t
−
d
i
s
[
i
]
∗
s
u
m
[
i
]
−
d
i
s
[
i
]
∗
s
u
m
[
j
]
−
d
i
s
[
j
]
∗
s
u
m
[
j
]
>
t
o
t
−
d
i
s
[
i
]
∗
s
u
m
[
i
]
−
d
i
s
[
i
]
∗
s
u
m
[
k
]
−
d
i
s
[
j
]
∗
s
u
m
[
k
]
tot-dis[i]*sum[i]-dis[i]*sum[j]-dis[j]*sum[j]>tot-dis[i]*sum[i]-dis[i]*sum[k]-dis[j]*sum[k]
tot−dis[i]∗sum[i]−dis[i]∗sum[j]−dis[j]∗sum[j]>tot−dis[i]∗sum[i]−dis[i]∗sum[k]−dis[j]∗sum[k]
最后得到
d
i
s
[
k
]
∗
s
u
m
[
k
]
−
d
i
s
[
j
]
∗
s
u
m
[
j
]
s
u
m
[
k
]
−
s
u
m
[
j
]
<
d
i
s
[
i
]
\frac{dis[k]*sum[k]-dis[j]*sum[j]}{sum[k]-sum[j]}<dis[i]
sum[k]−sum[j]dis[k]∗sum[k]−dis[j]∗sum[j]<dis[i]
可以发现,
d
i
s
dis
dis是单调递减的,也就是维护上凸壳,用单调队列实现
代码
#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);
}