@xiaoziyao
2020-09-07T23:41:24.000000Z
字数 2488
阅读 1173
解题报告
CF643C Levels and Regions解题报告:
比较好的dp题,把期望和斜率优化结合起来了。
首先,既然小时通过关卡的概率为,那么关卡期望个小时通过,设为的前缀和,那么关卡期望个小时通过。
然后,对于每一段,它的期望为
这里为了方便,我们把这一段改为,即。这样它的期望就是
我们设,为的前缀和;,为的前缀和。
这一段的期望可以直接改为
然后开始dp,我们设为从关卡处理到关卡,分段的最小期望。
那么很显然有转移:。
看到这个式子存在项,果断选择斜率优化。
设当时处理到段,且存在两个的决策点满足,且比优,那么有:
然后拆括号,移项:
因为,所以,所以可以两边同除,不变号:
把看成坐标,把看成坐标,这就是一个斜率的形式,直接上斜率优化板子就好了。
注意,因为这里是小于号,综合分析后应该取下凸壳。
时间复杂度:,可以通过本题。
#include<stdio.h>
#include<queue>
#define int long long
#define inf 10000000000000000
using namespace std;
const int maxn=200005,maxm=55;
int i,j,k,m,n,l,r;
int a[maxn],suma[maxn],q[maxn];
double b[maxn],c[maxn],sumb[maxn],sumc[maxn],f[maxm][maxn];
inline int x(int p){
return suma[p];
}
inline double y(int p,int c){
return f[c-1][p]+1.0*suma[p]*sumb[p]-sumc[p];
}
inline double slope(int a,int b,int c){
if(x(a)==x(b))
return y(a,c)>y(b,c)? inf:-inf;
return 1.0*(y(a,c)-y(b,c))/(x(a)-x(b));
}
signed main(){
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++){
scanf("%lld",&a[i]);
suma[i]=suma[i-1]+a[i];
b[i]=1.0/a[i],sumb[i]=sumb[i-1]+b[i];
c[i]=1.0*suma[i]/a[i],sumc[i]=sumc[i-1]+c[i];
}
for(i=1;i<=n;i++)
f[0][i]=inf;
for(i=1;i<=m;i++){
l=1,r=0;
q[++r]=0;
for(j=1;j<=n;j++){
while(l<r&&slope(q[l+1],q[l],i)<=sumb[j])
l++;
f[i][j]=f[i-1][q[l]]+sumc[j]-sumc[q[l]]-suma[q[l]]*(sumb[j]-sumb[q[l]]);
while(l<r&&slope(j,q[r-1],i)<=slope(q[r],q[r-1],i))
r--;
q[++r]=j;
}
}
printf("%.10lf\n",f[m][n]);
return 0;
}