Analysis
斜率优化dp好题。
对于第i只猫,显然如果管理员想从出发开始刚好接到它,需要在
t
[
i
]
=
h
[
i
]
−
d
i
s
t
(
1
,
i
)
t[i]=h[i]−dist(1,i)
t[i]=h[i]−dist(1,i)的时候出发才行。
这样的话,如果把第l~r只猫分成一组,那么当前分组需要的最小花费是
t
[
r
]
−
t
[
l
]
+
t
[
r
]
−
t
[
l
+
1
]
+
t
[
r
]
−
t
[
l
+
2
]
+
.
.
.
+
t
[
r
]
−
t
[
r
]
=
t
[
r
]
∗
(
r
−
l
+
1
)
−
(
s
u
m
[
r
]
−
s
u
m
[
l
−
1
]
)
t[r]−t[l]+t[r]−t[l+1]+t[r]−t[l+2]+...+t[r]−t[r]=t[r]∗(r−l+1)−(sum[r]−sum[l−1])
t[r]−t[l]+t[r]−t[l+1]+t[r]−t[l+2]+...+t[r]−t[r]=t[r]∗(r−l+1)−(sum[r]−sum[l−1])
于是就可以推出状态转移方程了:
f
[
i
]
[
j
]
=
m
i
n
(
f
[
i
−
1
]
[
k
]
+
a
[
j
]
∗
(
j
−
k
)
+
(
s
u
m
[
j
]
−
s
u
m
[
k
]
)
)
f[i][j]=min(f[i−1][k]+a[j]∗(j−k)+(sum[j]−sum[k]))
f[i][j]=min(f[i−1][k]+a[j]∗(j−k)+(sum[j]−sum[k]))
对于两个不同的决策k1,k2。
如果k1转移出的结果比k2优秀,那么:
f
[
i
−
1
]
[
k
1
]
+
a
[
j
]
∗
(
j
−
k
1
)
+
(
s
u
m
[
j
]
−
s
u
m
[
k
1
]
)
<
f
[
i
−
1
]
[
k
2
]
+
a
[
j
]
∗
(
j
−
k
2
)
+
(
s
u
m
[
j
]
−
s
u
m
[
k
2
]
)
f[i−1][k1]+a[j]∗(j−k1)+(sum[j]−sum[k1])<f[i−1][k2]+a[j]∗(j−k2)+(sum[j]−sum[k2])
f[i−1][k1]+a[j]∗(j−k1)+(sum[j]−sum[k1])<f[i−1][k2]+a[j]∗(j−k2)+(sum[j]−sum[k2])
=>
(
(
f
[
i
−
1
]
[
k
1
]
−
s
u
m
[
k
1
]
)
−
(
f
[
i
−
1
]
[
k
2
]
−
s
u
m
[
k
2
]
)
)
<
a
[
j
]
∗
(
k
1
−
k
2
)
((f[i−1][k1]−sum[k1])−(f[i−1][k2]−sum[k2]))<a[j]∗(k1−k2)
((f[i−1][k1]−sum[k1])−(f[i−1][k2]−sum[k2]))<a[j]∗(k1−k2)
假设
t
[
k
]
=
f
[
i
−
1
]
[
k
]
−
s
u
m
[
k
]
t[k]=f[i−1][k]−sum[k]
t[k]=f[i−1][k]−sum[k]
=>
(
t
[
k
1
]
−
t
[
k
2
]
)
/
(
k
1
−
k
2
)
<
a
[
j
]
(t[k1]−t[k2])/(k1−k2)<a[j]
(t[k1]−t[k2])/(k1−k2)<a[j]
果断斜率优化了。
<感谢>
注意初值
Code
#include<bits/stdc++.h>
#define ll long long
#define in read()
#define N 100009
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return f==1?res:-res;
}
int n,m,p;
int t,h;
ll d[N],a[N],s[N],f[105][N];
int l,r,q[N];
inline ll gety(int k,int i,int j){return f[k][i]+s[i]-f[k][j]-s[j];}
inline ll getx(int i,int j){return i-j;}
int main(){
n=in;m=in;p=in;
int i,j,k;
for(i=2;i<=n;++i) {
int tmp=in;
d[i]=d[i-1]+tmp;
}
for(i=1;i<=m;++i){
h=in;t=in;
a[i]=t-d[h];
}
sort(a+1,a+m+1);
for(i=1;i<=m;++i) s[i]=s[i-1]+a[i];
fill(f[0]+1,f[0]+m+1,1e18);//初值!!!!
ll ans=(1ll<<61);
for(i=1;i<=p;++i){
l=r=1;q[1]=0;
for(j=1;j<=m;++j){
// while(l<r&&f[i-1][q[l+1]]+s[q[l+1]]-f[i-1][q[l]]-s[q[l]]<a[j]*(q[l+1]-q[l])) l++;
while(l<r&&gety(i-1,q[l+1],q[l])<a[j]*getx(q[l+1],q[l]))++l;
int t=q[l];
// printf("%d l=%d\n",t,l);
f[i][j]=f[i-1][t]+a[j]*(j-t)-s[j]+s[t];
// while(l<r&&(f[i-1][q[r]]+s[q[r]]-f[i-1][q[r-1]]-s[q[r-1]])*(j-q[r])>(f[i-1][j]+s[j]-f[i-1][q[r]]-s[q[r]])*(q[r]-q[r-1])) r--;
while(l<r&&gety(i-1,q[r],q[r-1])*getx(j,q[r])>gety(i-1,j,q[r])*getx(q[r],q[r-1]))--r;
q[++r]=j;
}
ans=min(ans,f[i][m]);
}
cout<<ans;
return 0;
}