ICPC 焦作 Sequence

ICPC 焦作 Sequence

https://nanti.jisuanke.com/t/31713

题目是给定 1<m<250,1<n<109 1 < m < 250 , 1 < n < 10 9
[1,n] [ 1 , n ] 中等概率取 m m 个数字,组成一个非递减序列,记f(i) i i 出现的次数 .

计算下式期望
maxi=1nf(i)

显然,顺序不重要,考虑计算所有可能的总情况的数量,即方程:

f(1)+f(2)+f(3)+...+f(n)=m f ( 1 ) + f ( 2 ) + f ( 3 ) + . . . + f ( n ) = m

对应的总答案数。
这个方程答案数量为(对m个1进行插入n-1个挡板,挡板之间的1的个数对应f):
(n+m1m) ( n + m − 1 m )
dp[m][x][t] d p [ m ] [ x ] [ t ] 为将 m m 拆分成t个数字,最大数字为 x x 的所有排列。

那么我们将这t个数随机分配给 f f ,未分配的f的是 0 0 ,那么分配方案显然是:
(nt)

记: P(x) P ( x ) maxni=1 f(i)=x m a x i = 1 n   f ( i ) = x 的概率。
P(x)=t=1mdp[m][x][t](nt)(n+m1m) P ( x ) = ∑ t = 1 m d p [ m ] [ x ] [ t ] ( n t ) ( n + m − 1 m )
下面考虑计算 dp d p 数组。
显然, dp[m][x][t] d p [ m ] [ x ] [ t ] 可以分两部贡献:
第一部分,最后一个数字拆分小于x,那么之后的拆分必须出现一个x:
k=1x1dp[nk][x][t1] ∑ k = 1 x − 1 d p [ n − k ] [ x ] [ t − 1 ]
A[m][x][t] A [ m ] [ x ] [ t ] 表示拆分最大数字不超过 x x 的所有排列:

那么第二部分贡献为:
A[mx][x][t1]

对于 A[m][x][t] A [ m ] [ x ] [ t ] 有:
A[m][x][t]=k=1xA[mk][x][t1] A [ m ] [ x ] [ t ] = ∑ k = 1 x A [ m − k ] [ x ] [ t − 1 ]
考虑压缩 t t ,并递推过程中记录。B[x][t]=dp[m][x][t]

这样就有了概率密度函数。

则答案为:
ans=x=1mxP(x)=x=1mxt=1mB[x][t](nt)(n+m1m) a n s = ∑ x = 1 m x P ( x ) = ∑ x = 1 m x ∑ t = 1 m B [ x ] [ t ] ( n t ) ( n + m − 1 m )
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define MAXN  252
using namespace std;

long double C;
double dp[MAXN][MAXN][2];
double Sd[MAXN][MAXN][2];
double Sa[MAXN][MAXN][2];
double A[MAXN][MAXN][2];
double B[MAXN][MAXN];
int main()
{
    int n, m;
    while (scanf("%d %d", &m, &n) == 2)
    {
        memset(dp, 0, sizeof dp);
        memset(Sa, 0, sizeof Sa);
        memset(Sd, 0, sizeof Sd);
        memset(A, 0, sizeof A);
        memset(B, 0, sizeof B);
        for (int i = 0;i <= m;i++)A[0][i][0] = 1;
        for (int i = 0;i <= m;i++)
            for (int j = 0;j <= m;j++)Sa[i][j][0] = 1;
        for (int i = 0;i <= m;i++)Sd[i][0][0] = 1;
        dp[0][0][0] = 1;
        bool ansf = false;
        for (int t = 1;t <= m;t++, ansf = !ansf)
        {
            for (int x = 0;x <= m;x++)A[0][x][!ansf] = 0;
            for (int i = 0;i <= m;i++)
                for (int x = 0;x <= m;x++)Sd[i][x][!ansf] = Sa[i][x][!ansf] = 0;
            for (int i = 1;i <= m;i++)
            {
                for (int x = 1;x <=m;x++)
                {
                    if (x > i)
                    {
                        dp[i][x][!ansf] = 0;
                        A[i][x][!ansf] = A[i][i][!ansf];
                        Sa[i][x][!ansf] = Sa[i - 1][x][!ansf] + A[i][x][!ansf];
                        Sd[i][x][!ansf] = Sd[i - 1][x][!ansf];
                        continue;
                    }
                    A[i][x][!ansf] = Sa[i - 1][x][ansf];
                    if (i > x)A[i][x][!ansf] -= Sa[i - x - 1][x][ansf];
                    dp[i][x][!ansf] = A[i - x][x][ansf] + Sd[i - 1][x][ansf] - Sd[i - x][x][ansf];
                    Sd[i][x][!ansf] = Sd[i - 1][x][!ansf] + dp[i][x][!ansf];
                    Sa[i][x][!ansf] = Sa[i - 1][x][!ansf] + A[i][x][!ansf];
                }
                if (i == m)
                    for (int x = 1;x <= m;x++)  B[x][t] = dp[m][x][!ansf];
            }
        }
        C = 1;
        long double ans = 0;
        for (int k = 1;k <= m;k++)C *= ((long double)k) / ((long double)n - 1 + k);
        for (int t = 1;t <= m&&t <= n;t++)
        {
            C *= (long double)(n - t + 1) / (long double)t;
            for (int x = 1;x <= m;x++)
            {
                //if (B[x][t])printf("%lld\n", B[x][t]);
                ans += x*B[x][t] * C;
            }
        }
        printf("%.4f\n", (double)ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值