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
个数字,组成一个非递减序列,记为
i
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+m−1m)
(
n
+
m
−
1
m
)
记
dp[m][x][t]
d
p
[
m
]
[
x
]
[
t
]
为将
m
m
拆分成个数字,最大数字为
x
x
的所有排列。
那么我们将这个数随机分配给
f
f
,未分配的的是
0
0
,那么分配方案显然是:
记: 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+m−1m)
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=1x−1dp[n−k][x][t−1]
∑
k
=
1
x
−
1
d
p
[
n
−
k
]
[
x
]
[
t
−
1
]
记
A[m][x][t]
A
[
m
]
[
x
]
[
t
]
表示拆分最大数字不超过
x
x
的所有排列:
那么第二部分贡献为:
对于
A[m][x][t]
A
[
m
]
[
x
]
[
t
]
有:
A[m][x][t]=∑k=1xA[m−k][x][t−1]
A
[
m
]
[
x
]
[
t
]
=
∑
k
=
1
x
A
[
m
−
k
]
[
x
]
[
t
−
1
]
考虑压缩 t t ,并递推过程中记录。
这样就有了概率密度函数。
则答案为:
ans=∑x=1mxP(x)=∑x=1mx∑t=1mB[x][t](nt)(n+m−1m)
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;
}