【校内模拟】assignment(DP)

题面见校内OJ4693


题解:

考虑预处理f[k][i][j]f[k][i][j]表示最长的一段不超过kk的时候,将长度为ii的序列分为jj段的方案数。

kk相同的状态之间转移,显然有f[i][j]=f[i1][j]+f[i1][j1]f[ik1][j1]f[i][j]=f[i-1][j]+f[i-1][j-1]-f[i-k-1][j-1],考虑新的这个数是否放在原来的最后一个段中就行了。

然后分成jj段,显然就需要从nn种不同的数中选择jj种就行了。

用long double可以避免炸精度。

然而我考场上觉得可能会炸就没写。

痛失难得的一次AK机会

TM又是只A了T2T3就差T1的一场考试


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

typedef long double ld;

cs int N=251;
ld f[N][N][N];
ld sum[N],C[N];
inline void init(){
	for(int re k=1;k<N;++k){
		f[k][0][0]=1;
		for(int re i=1;i<N;++i)
		for(int re j=1;j<=i;++j){
			f[k][i][j]=f[k][i-1][j]+f[k][i-1][j-1];
			if(i>=k+1)f[k][i][j]-=f[k][i-k-1][j-1];
		}
	}
}

inline void solve(){
	int n,m;scanf("%d%d",&m,&n);
	C[0]=1;for(int re i=1;i<=m;++i)C[i]=C[i-1]*(n-i+1)/i;
	for(int re k=1;k<=m;++k){
		sum[k]=0;
		for(int re j=1;j<=m;++j)
		sum[k]+=f[k][m][j]*C[j];
	}ld ans=0,tot=sum[m];
	for(int re i=1;i<=m;++i){
		ans+=(sum[i]-sum[i-1])*i/tot;
	}
	printf("%.7lf\n",(double)ans);
}

signed main(){
#ifdef zxyoi
	freopen("assignment.in","r",stdin);
#endif
	int T;scanf("%d",&T);init();
	while(T--)solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章