• IOI2021集训队作业 123ED Gem Island


    题目相当于:有个序列({a_i}),一开始全(0)。有(d)次操作,每次随机地选一个(i),概率为(frac{a_i}{sum a_i}),然后将(a_i)变成(a_i+1)

    求最终前(r)个期望是多少。

    答案输出小数。


    早就想到了大概的思路,但因为答案输出小数而不是取模迟迟不敢下手。因为这题在计数过程中铁定会出现很大的数的,然后最后再除以一个很大的组合数。看起来精度问题就很可怕。

    后来翻翻题解发现原来没有任何精度的问题。。

    假如最后形成的有序序列为({a_i}),得到它的概率为(frac{1}{prod_{i=n}^{n+d-1}i}prod a_i!frac{(sum a_i)!}{prod a_i!}),即(frac{1}{inom{n+d-1}{n}})

    于是只需要统计每个有序序列的贡献。转化成统计无序序列(排序后)的方案数和贡献。

    有如此DP:(f_{i,j})表示(i)个数和为(j)的方案数,(g_{i,j})(i)个数和为(j)的前(r)个数的和。

    转移考虑枚举(k)(k)个至少为(1)(i-k)个恰好为(0)

    于是有(f_{k,j-k}inom{i}{k} o f_{i,j})(g_{k,j-k}inom{i}{k}+f_{k,j-k}inom{i}{k}min(k,r) o g_{i,j})

    直接用long double算没有爆精度,佩服。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define db long double
    #define N 1005
    int n,d,r;
    db C[N][N];
    void initC(int n){
    	for (int i=0;i<=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];
    	}
    }
    db f[N][N],g[N][N];
    int main(){
    	scanf("%d%d%d",&n,&d,&r);
    	initC(n+d);
    	f[0][0]=1,g[0][0]=0;
    	for (int i=1;i<=n;++i){
    		for (int j=0;j<=d;++j)
    			for (int k=0;k<=j && k<=i;++k){
    				f[i][j]+=f[k][j-k]*C[i][k];
    				g[i][j]+=g[k][j-k]*C[i][k]+min(k,r)*f[k][j-k]*C[i][k];
    			}
    	}
    	printf("%.7lf
    ",(double)(g[n][d]/C[n+d-1][d])+r);
    	return 0;
    }
    
  • 相关阅读:
    Kafka写入流程和副本策略
    Kafka消费分组和分区分配策略
    Kafka安装和常用操作命令
    面试:谈谈你对大数据的理解
    HDFS写数据和读数据流程
    java笔记之分支、循环语句
    java笔记之分支、循环语句
    java笔记之运算符
    java笔记之运算符
    java笔记之java内存结构
  • 原文地址:https://www.cnblogs.com/jz-597/p/13848432.html
Copyright © 2020-2023  润新知