Gym - 103119L的另类解法

发布时间 2023-11-09 09:43:42作者: 蒻蒟cdx

题意

有一个长为\(n(n<=50)\)的整数序列\(A\),每个数都是随机生成的,并且每个数在\(1-n\)的范围内等概率生成。你的任务是计算有多少长度为\(n\)的排列(值域是\(1-n\))任意位置满足\(p_i<=a_i\),求期望的排列数量

输出要求

答案至少保留标准答案的前九位

解法1

玩两组数据发现答案是\(\frac{(n!)^2}{n^n}\),直接算就完了……

解法2

由于本人太蒻了,模拟赛上只想到了\(O(n^5)\)的dp

先进行一步转化,考虑\(1-n\)之间的数在\(A\)中出现的次数,记其为\(b_i\)

显然有\(b_i\)的前缀和要小于等于\(i\),否则答案为\(0\)

考虑设\(dp_{i,j,k}\)表示\(1~i\)中的数选了\(j\)个,\(1-i\)的排列有\(k\)个不能选的方案数

转移方程为\(dp_{i,j,k}=\sum\limits_{x=1}^j\sum\limits_{y=0}^{x-1}dp_{i-1,j-x,k+y}*A_{k+y+1}^{y+1}*C_j^x;\)

如果\(k>0\)的话还要加上\(dp_{i-1,j,k-1}\)

意思是选x个物品i,\(i\)在排列中覆盖了\(y\)个,后面的两个组合数分别为排列的方案数和把前\(i\)个数在序列\(A\)中放置的方案数,不理解的话可以手玩一下(我推了2h)

代码

#include<bits/stdc++.h>
using namespace std;
int n;
double dp[61][61][61],C[110][110],a[110];
int main(){
	cin>>n;
	a[0]=1;
	for(int i=1;i<=n;i++) a[i]=a[i-1]*i;
	C[0][0]=1;
	for(int i=1;i<=2*n;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
	dp[1][1][0]=1.0/n;
	dp[1][0][1]=1.0/n;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=i;j++){
			for(int k=0;k<=i;k++){
				if(k) dp[i][j][k]=dp[i-1][j][k-1];
				for(int x=1;x<=j;x++){
					for(int y=0;y<x;y++){
						dp[i][j][k]+=dp[i-1][j-x][k+y]*C[k+y+1][y+1]*a[y+1]*C[j][x];
					}
				}
				dp[i][j][k]/=n;
			}
		}
	}
	printf("%.9lf\n",dp[n][n][0]);
	return 0;
} 

后记

为什么大家都想到了性质啊,我太菜了