题目大意
卖足球赛的票,票价是 10元/张,你手中有 k k k 张 10 元钱,并且知道有 n n n 个人来买票时会带 10 元, m m m 个人会带 20 元钱。你可以顺利卖票,即每次都能找得出钱的概率是多少。
Solution
首先分析顺利卖票,由于票价是 10 元,所以带 10 元的人是不需要你找钱,相反,他们来买票还可以为你提供找 20 元的钱。所以,得出结论,10 元是好的,20 元是不好的。
我们可以先算有多少合法的序列,然后除以总方案即可。
于是,我们开始转化模型。
step 1
由上面的结论可以轻易推出,如果一种买票的顺序,使得某一次,之前带 20 元来买票的人比带 10 元的人加上原有的 k k k 张 10 元还要多了,那么就找不开钱,也就是没有顺利卖票了。
这很容易联想到栈,进一步的,就是典型的卡特兰数的变形。
问题就是,栈中原有 k k k 个元素,现在有 n n n 次入栈操作, m m m 次出栈操作,问有多少种操作顺序。
step 2
显然,这样仍不足以解决这个问题,因为毕竟不是卡特兰数的模板。我们回顾一下卡特兰数的推导:
在一个坐标系中,从原点出发,可以向上走 m m m 步,向右走 n n n 步,请问有多少种方式,可以不越过直线 y = x y=x y=x 并且到达点 ( n , m ) (n,m) (n,m)。
在这题中,可以把初始的 k k k 个元素转化为:在坐标轴上,从坐标 ( k , 0 ) (k,0) (k,0) 出发。
所以题目又转化为:
在一个直角坐标系中,从点 (0,k) 出发,可以向上 m 或向右 n 步,且必须在直线 y=x 的下方(包括边界),求有多少种合法的非降路径。
step 3
最后,为了解决上面的问题,我们仍然从卡特兰数的推导中获得启发。即利用 P ( A ) = 1 − P ( A ‾ ) P(A)=1-P(\overline{A}) P(A)=1−P(A)。
直接算不合法的概率,当然首要还是先算不合法的方案数。
显然,可以先得出总方案数是
C
n
+
m
n
C_{n+m}^n
Cn+mn,其中不合法的方案,反映在坐标轴上一定是经过直线
y
=
x
+
1
y=x+1
y=x+1 的,由卡特兰数的路径计数推导,可以得出,我们只要将起点关于直线
y
=
x
+
1
y=x+1
y=x+1 对称,即将起点变成
(
−
1
,
k
+
1
)
(-1,k+1)
(−1,k+1),然后可以得出,不合法的方案数是
C
n
+
m
k
+
n
+
1
C_{n+m}^{k+n+1}
Cn+mk+n+1。因此不合法的概率就是:
C
n
+
m
k
+
n
+
1
C
n
+
m
n
\frac{C_{n+m}^{k+n+1}}{C_{n+m}^n}
Cn+mnCn+mk+n+1
因此合法的概率就是:
1
−
C
n
+
m
k
+
n
+
1
C
n
+
m
n
1-\frac{C_{n+m}^{k+n+1}}{C_{n+m}^n}
1−Cn+mnCn+mk+n+1
=
1
−
(
n
+
m
)
!
(
m
−
k
−
1
)
!
(
k
+
n
+
1
)
!
(
n
+
m
)
!
n
!
m
!
=1-\frac{\frac{(n+m)!}{(m-k-1)!(k+n+1)!}}{\frac{(n+m)!}{n!m!}}
=1−n!m!(n+m)!(m−k−1)!(k+n+1)!(n+m)!
=
1
−
n
!
m
!
(
m
−
k
−
1
)
!
(
k
+
n
+
1
)
!
=1-\frac{n!m!}{(m-k-1)!(k+n+1)!}
=1−(m−k−1)!(k+n+1)!n!m!
所以最后的代码就是个式子,很短。
Code
#include<bits/stdc++.h>
#define ll long long
#define inf 1<<30
#define INF 1ll<<60
#define pb push_back
using namespace std;
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
if(n+k<m){puts("0");return 0;}//先判无解
int up=m,down=k+n+1;
double ans=1;
for(int i=0;i<=k;i++,up--,down--)
ans=ans*1.0*up/down;
printf("%.5lf\n",1-ans);
}