Codeforces 674C Levels and Regions

题目大意:

把一段长为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\n",f[m][n]);
	return 0;
}

参考:

posted @ 2021-08-10 20:43  GUO_dx  阅读(42)  评论(0编辑  收藏  举报