CF26D Tickets(dp + 组合数)
Description
你有 k k k 张 10 10 10 元的钞票,有 n n n 个客人手持 10 10 10 元的钞票,还有 m m m 个客人手持 20 20 20 元的钞票。 n + m n+m n+m 个客人会随机排队买票,求所有的客人都能找开钱的概率。
Solution
有两个方法,组合数和 dp。先考虑组合数,容易转换成从
(
k
,
0
)
→
(
n
+
k
,
m
)
(k,0) \to (n+k,m)
(k,0)→(n+k,m) 只允许向上向右走,求不越过
y
=
x
y=x
y=x 的方案数。如果没有不越过的限制,那么是
(
n
+
m
m
)
n+m \choose m
(mn+m),如果有限制那么减去不合法的部分。
将
y
=
x
y=x
y=x 向上平移得到
y
=
x
+
1
y=x+1
y=x+1。作
(
k
,
0
)
(k,0)
(k,0) 关于
y
=
x
+
1
y=x+1
y=x+1 的对称点
(
−
1
,
k
+
1
)
(-1,k+1)
(−1,k+1)。那么从
(
−
1
,
k
+
1
)
→
(
n
+
k
,
m
)
(-1,k+1) \to (n+k,m)
(−1,k+1)→(n+k,m) 的所有路径都有
(
k
,
0
)
→
(
n
+
k
,
m
)
(k,0) \to(n+k,m)
(k,0)→(n+k,m) 中与之对应的路径,而从
(
−
1
,
k
+
1
)
→
(
n
+
k
,
m
)
(-1,k+1) \to (n+k,m)
(−1,k+1)→(n+k,m) 的所有路径都越过了
y
=
x
y=x
y=x。所以方案数为
( n + m m ) − ( n + m m − k − 1 ) {n+m \choose m} - {n+m\choose m-k-1} (mn+m)−(m−k−1n+m)
那么概率为合法方案与所有方案之比
a n s = ( n + m m ) − ( n + m m − k − 1 ) ( n + m m ) = ( n + m ) ! n ! × m ! − ( n + m ) ! ( n + k + 1 ) ! × ( m − k − 1 ) ! ( n + m ) ! n ! × m ! = 1 − n ! × m ! ( n + k + 1 ) ! × ( m − k − 1 ) ! = 1 − n ! × m ! × ∏ i = m − k m i n ! × ∏ i = n + 1 n + k + 1 i × m ! = 1 − ∏ i = m − k m i ∏ i = n + 1 n + k + 1 i ans = \frac{{n+m \choose m} - {n+m\choose m-k-1}}{{n+m \choose m}} \\ \ \\ = \frac{ \frac{(n+m)!}{n!\times m!} - \frac{(n+m)!}{(n+k+1)!\times (m-k-1)!}}{\frac{(n+m)!}{n!\times m!}}\\ \ \\ = 1- \frac{n!\times m!}{(n+k+1)!\times (m-k-1)!} \\ \ \\ = 1- \frac{n!\times m! \times \prod_{i=m-k}^{m} i}{n! \times \prod_{i=n+1}^{n+k+1} i \times m! }\\ \ \\ = 1- \frac{\prod_{i=m-k}^{m} i}{\prod_{i=n+1}^{n+k+1} i} ans=(mn+m)(mn+m)−(m−k−1n+m) =n!×m!(n+m)!n!×m!(n+m)!−(n+k+1)!×(m−k−1)!(n+m)! =1−(n+k+1)!×(m−k−1)!n!×m! =1−n!×∏i=n+1n+k+1i×m!n!×m!×∏i=m−kmi =1−∏i=n+1n+k+1i∏i=m−kmi
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, INF = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
int main() {
int n = read(), m = read(), k = read();
if (n + k < m) {
puts("0"); return 0;
}
double ans = 1;
for (int i = 1; i <= k + 1; i++) ans *= 1.0 * (i + m - k - 1) / (n + i);
printf("%lf", 1 - ans);
return 0;
}

浙公网安备 33010602011771号