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)(mk1n+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)(mk1n+m) =n!×m!(n+m)!n!×m!(n+m)!(n+k+1)!×(mk1)!(n+m)! =1(n+k+1)!×(mk1)!n!×m! =1n!×i=n+1n+k+1i×m!n!×m!×i=mkmi =1i=n+1n+k+1ii=mkmi

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;
}
posted @ 2020-02-29 20:28  ylxmf2005  阅读(27)  评论(0)    收藏  举报