题目链接:点击这里
题目大意:
P
P
P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
P
P
P 教授有编号为
1
⋯
n
1 \cdots n
1⋯n 的
n
n
n 件玩具,第
i
i
i 件玩具经过压缩后的一维长度为
C
i
C_i
Ci。
为了方便整理,
P
P
P教授要求:
1.在一个一维容器中的玩具编号是连续的。
2.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第
i
i
i 件玩具到第
j
j
j 个玩具放到一个容器中,那么容器的长度将为
x
=
j
−
i
+
∑
k
=
i
j
C
k
x=j-i+\sum\limits_{k=i}^{j}C_k
x=j−i+k=i∑jCk。
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx,其制作费用为
(
x
−
L
)
2
(x-L)^2
(x−L)2 。其中 LL 是一个常量。
P
P
P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过
L
L
L 。但他希望所有容器的总费用最小。
题目分析:
设
d
p
[
i
]
dp[i]
dp[i] 为前
i
i
i 个装箱的最小花费
故有转移方程:
d
p
[
i
]
=
m
i
n
j
=
0
i
−
1
(
d
p
[
j
]
+
(
s
u
m
[
i
]
+
i
−
s
u
m
[
j
]
−
j
−
L
−
1
)
2
)
dp[i]=min_{j=0}^{i-1}(dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2)
dp[i]=minj=0i−1(dp[j]+(sum[i]+i−sum[j]−j−L−1)2)
其中
s
u
m
[
i
]
=
∑
j
=
1
i
C
i
sum[i]=\sum_{j=1}^iC_i
sum[i]=∑j=1iCi
这个方程的时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)
我们考虑优化:
斜率优化:
设
G
[
i
,
j
]
G[i,j]
G[i,j] 为
d
p
[
i
]
dp[i]
dp[i] 的一个决策点,即
G
[
i
,
j
]
=
d
p
[
j
]
+
(
s
u
m
[
i
]
+
i
−
s
u
m
[
j
]
−
j
−
L
−
1
)
2
G[i,j]=dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2
G[i,j]=dp[j]+(sum[i]+i−sum[j]−j−L−1)2
记
F
[
x
]
=
s
u
m
[
x
]
+
x
,
S
=
L
+
1
F[x]=sum[x]+x,S=L+1
F[x]=sum[x]+x,S=L+1
故
G
[
i
,
j
]
=
d
p
[
j
]
+
(
F
[
i
]
−
F
[
j
]
−
S
)
2
G[i,j]=dp[j]+(F[i]-F[j]-S)^2
G[i,j]=dp[j]+(F[i]−F[j]−S)2
=
d
p
[
j
]
+
F
[
i
]
2
−
2
F
[
i
]
∗
S
−
2
F
[
i
]
∗
F
[
j
]
+
(
F
[
j
]
+
S
)
2
=dp[j]+F[i]^2-2F[i]*S-2F[i]*F[j]+(F[j]+S)^2
=dp[j]+F[i]2−2F[i]∗S−2F[i]∗F[j]+(F[j]+S)2
设
a
,
b
a,b
a,b 为为
d
p
[
i
]
dp[i]
dp[i] 的两个决策点且
b
b
b 点比
a
a
a 点更优(不妨令
a
<
b
a<b
a<b),则有
G
[
i
,
a
]
>
G
[
i
,
b
]
G[i,a]>G[i,b]
G[i,a]>G[i,b]
即
d
p
[
a
]
+
F
[
i
]
2
−
2
F
[
i
]
∗
S
−
2
F
[
i
]
∗
F
[
a
]
+
(
F
[
a
]
+
S
)
2
>
d
p
[
b
]
+
F
[
i
]
2
−
2
F
[
i
]
∗
S
−
2
F
[
i
]
∗
F
[
b
]
+
(
F
[
b
]
+
S
)
2
dp[a]+F[i]^2-2F[i]*S-2F[i]*F[a]+(F[a]+S)^2>dp[b]+F[i]^2-2F[i]*S-2F[i]*F[b]+(F[b]+S)^2
dp[a]+F[i]2−2F[i]∗S−2F[i]∗F[a]+(F[a]+S)2>dp[b]+F[i]2−2F[i]∗S−2F[i]∗F[b]+(F[b]+S)2
等价于
F
[
i
]
∗
(
F
[
b
]
−
F
[
a
]
)
>
d
p
[
b
]
+
(
F
[
b
]
+
S
)
2
−
d
p
[
a
]
−
(
F
[
a
]
+
S
)
2
F[i]*(F[b]-F[a])>dp[b]+(F[b]+S)^2-dp[a]-(F[a]+S)^2
F[i]∗(F[b]−F[a])>dp[b]+(F[b]+S)2−dp[a]−(F[a]+S)2
等价于
F
[
i
]
>
d
p
[
b
]
+
(
F
[
b
]
+
S
)
2
−
d
p
[
a
]
−
(
F
[
a
]
+
S
)
2
F
[
b
]
−
F
[
a
]
F[i]>\frac{dp[b]+(F[b]+S)^2-dp[a]-(F[a]+S)^2}{F[b]-F[a]}
F[i]>F[b]−F[a]dp[b]+(F[b]+S)2−dp[a]−(F[a]+S)2 (因为
F
[
x
]
=
s
u
m
[
x
]
+
x
F[x]=sum[x]+x
F[x]=sum[x]+x 所以
F
[
b
]
−
F
[
a
]
>
0
F[b]-F[a]>0
F[b]−F[a]>0)
令
Y
[
x
]
=
d
p
[
x
]
+
(
F
[
x
]
+
S
)
2
Y[x]=dp[x]+(F[x]+S)^2
Y[x]=dp[x]+(F[x]+S)2
故原式等价于
F
[
i
]
>
Y
[
b
]
−
Y
[
a
]
F
[
b
]
−
F
[
a
]
F[i]>\frac{Y[b]-Y[a]}{F[b]-F[a]}
F[i]>F[b]−F[a]Y[b]−Y[a] (
F
[
i
]
F[i]
F[i] 显然是一个单调递增的函数)
有了这个式子我们就可以通过维护一个下凸包来进行斜率优化了(斜率小转移点的更优秀,推倒过程可以参照这篇博客点击这里)
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
ll res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
ll n,s,f[maxn],dp[maxn],qu[maxn],h,t;
ll Y(int x)
{
return dp[x]+(f[x]+s)*(f[x]+s);
}
double calc(int i,int j)
{
return (double)(Y(i)-Y(j))/(f[i]-f[j]);
}
int main()
{
n = read(),s = read();
s++;//处理 S
for(int i = 1;i <= n;i++)
{
f[i] = read();
f[i] += f[i-1]+1;//处理 F[x]数组
}
h = t = 1;ll j;
for(int i = 1;i <= n;i++)
{
while(h < t && calc(qu[h],qu[h+1]) <= 2*f[i]) h++;
int j = qu[h];
dp[i] = dp[j]+(f[i]-f[j]-s)*(f[i]-f[j]-s);
while(h < t && calc(qu[t-1],qu[t]) >= calc(qu[t-1],i)) t--;
qu[++t] = i;
}
printf("%lld\n",dp[n]);
return 0;
}