梗概
目前只会斜率优化的naive的版本,动态维护凸包的版本目前不会,以后会的时候再更新
我们在做dp问题时经常性的会发现时间复杂度完全不够,那么我们需要用到优化了
dp的优化有很多,这里不再赘述,单说斜率优化。
总条件
f[i]=max(f[k]+g[i]*g[k]+b)(有决策点k的状态,有i和k的状态,有常数b)
然后将常数项和当前要求的f[i]算作B
B=f[i]+b
B
=
f
[
i
]
+
b
将只与f[k]有关的算作y
y=f[k]+...
y
=
f
[
k
]
+
.
.
.
将与g[k]和g[i]有关的 g[k]算作x g[i]算作k
B=y+k∗x==>y=B−k∗x
B
=
y
+
k
∗
x
==>
y
=
B
−
k
∗
x
然后就是类似于数学中的线性规划问题,用一条斜率是定值的直线求和之前状态构成的凸包的交点,交点就是当前的决策点。
情况一:若决策的横坐标
x[j]
x
[
j
]
和斜率
k
k
单调,我们只需维护一个单调队列
每次类似算法一样加点、删点
询问时根据斜率单调移动询问指针
时间复杂度为
O(n)
O
(
n
)
情况二:若决策的横坐标
x[j]
x
[
j
]
和斜率
k
k
不单调,我们则需要一个高级数据结构来动态维护凸包,显然平衡树能够胜任
加点时我们需要二分横坐标位置,并左右删点以维护凸壳凸性
在询问时则需二分凸壳的斜率以得到直线与凸壳的切点
时间复杂度为
引用刘明华dalao的课件内容。
[HNOI2008]玩具装箱toy
一道较为好想的dp题
暴力异常好想:
f[j]=min(f[k]+(j−k−1+sum[j]−sum[k]−L)2)
f
[
j
]
=
m
i
n
(
f
[
k
]
+
(
j
−
k
−
1
+
s
u
m
[
j
]
−
s
u
m
[
k
]
−
L
)
2
)
下面稍微整一下
g[i]=sum[i]+i;L=L+1;
g
[
i
]
=
s
u
m
[
i
]
+
i
;
L
=
L
+
1
;
方便讨论
(g[j]−g[k]−L)2=g[j]∗g[j]−2∗g[j]∗L+L∗L+g[k]∗g[k]−2∗g[k]∗g[j]+2∗L∗g[k];
(
g
[
j
]
−
g
[
k
]
−
L
)
2
=
g
[
j
]
∗
g
[
j
]
−
2
∗
g
[
j
]
∗
L
+
L
∗
L
+
g
[
k
]
∗
g
[
k
]
−
2
∗
g
[
k
]
∗
g
[
j
]
+
2
∗
L
∗
g
[
k
]
;
移一下项
f[j]+2∗g[j]∗L−L∗L−g[j]∗g[j]=f[k]+2∗L∗g[k]+g[k]∗g[k]−2∗g[k]∗g[j]
f
[
j
]
+
2
∗
g
[
j
]
∗
L
−
L
∗
L
−
g
[
j
]
∗
g
[
j
]
=
f
[
k
]
+
2
∗
L
∗
g
[
k
]
+
g
[
k
]
∗
g
[
k
]
−
2
∗
g
[
k
]
∗
g
[
j
]
B=f[j]+2∗g[j]∗L−L∗L−g[j]∗g[j];
B
=
f
[
j
]
+
2
∗
g
[
j
]
∗
L
−
L
∗
L
−
g
[
j
]
∗
g
[
j
]
;
y=f[k]+g[k]∗g[k]+2∗L∗g[k];
y
=
f
[
k
]
+
g
[
k
]
∗
g
[
k
]
+
2
∗
L
∗
g
[
k
]
;
k=−2∗g[j];
k
=
−
2
∗
g
[
j
]
;
x=g[k];
x
=
g
[
k
]
;
B=y+kx;
B
=
y
+
k
x
;
y=−kx+B;
y
=
−
k
x
+
B
;
这样就可以dp优化了,用单调队列维护
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=50000+10;
ll f[maxn],g[maxn],sum[maxn],a[maxn];
int n;
ll L;
struct node
{
ll x,y; int id;
}q[maxn];
double getk(int x,int y)
{
return (q[y].y-q[x].y)*1.0/(q[y].x-q[x].x);
}
int main()
{
freopen("bzoj_1010.in","r",stdin);
freopen("bzoj_1010.out","w",stdout);
scanf("%d %lld",&n,&L); L++;
for(int i=1; i<=n; i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i]; g[i]=sum[i]+i;
}
int head=0,tail=0;
for(int i=1; i<=n; i++)
{
while(head<tail && getk(head,head+1)<=2*g[i]) head++;
f[i]=-2*g[i]*q[head].x+q[head].y-2*g[i]*L+L*L+g[i]*g[i];
ll y=f[i]+g[i]*g[i]+2*L*g[i],x=g[i];
while(tail>head && ((y-q[tail-1].y)*1.0/(x-q[tail-1].x))<getk(tail,tail-1)) tail--;
tail++; q[tail].y=y; q[tail].x=x; q[tail].id=i;
// cout<<f[i]<<endl;
}
cout<<f[n]<<endl;
return 0;
}