题目大意:
把一段长为n的序列分成k组(每组是连续的一段),然后玩家不停地操作:
- step 1:
找到第一个没被全灭的组 - step 2:
把这个组里灭了的 t[i] 全加起来,然后把新的没被灭的 t[i+1]也加进去 - step 3:
最后从Σt中随便选一个,删去它代表的位上的数。意思就是第i+1个数被删的概率是t[i+1]/Σt.
求把所有数删完的操作次数的min期望。
首先期望逆推一下,
(fi=(1-p)*fi+p*f[i+1]+1)
((f[n+1]=0))
化简变成
(fi=f[i+1]+1/p)
(p=t[i]/Σt)
所以 第 k组消灭L到R的数的期望是
(t[L]/t[L]+(t[L]+t[L+1])/t[L+1]+......+Σt/t[R])
(设DP[i][j]为第i组,最后一个数是j,那么 DP[i][j]=min{DP[i][j],DP[i-1][w]+期望[w+1,j]})
复杂度(O(k*n*n)),GG。
首先优化期望的求法,(Σ(sum[i]-sum[L-1])/t[i] = Σ(sum[i]/t[i])-sum[L-1]*Σ(1/t[i]))
code
#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
",f[m][n]);
return 0;
}