@xiaoziyao
2020-09-07T15:41:24.000000Z
字数 2488
阅读 1574
解题报告
CF643C Levels and Regions解题报告:
比较好的dp题,把期望和斜率优化结合起来了。
首先,既然小时通过关卡的概率为,那么关卡期望个小时通过,设为的前缀和,那么关卡期望个小时通过。
然后,对于每一段,它的期望为
这里为了方便,我们把这一段改为,即。这样它的期望就是
我们设,为的前缀和;,为的前缀和。
这一段的期望可以直接改为
然后开始dp,我们设为从关卡处理到关卡,分段的最小期望。
那么很显然有转移:。
看到这个式子存在项,果断选择斜率优化。
设当时处理到段,且存在两个的决策点满足,且比优,那么有:
然后拆括号,移项:
因为,所以,所以可以两边同除,不变号:
把看成坐标,把看成坐标,这就是一个斜率的形式,直接上斜率优化板子就好了。
注意,因为这里是小于号,综合分析后应该取下凸壳。
时间复杂度:,可以通过本题。
#include<stdio.h>#include<queue>#define int long long#define inf 10000000000000000using 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;}
