uestc1888 Birthday Party    组合数学,乘法原理

题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25539#problem/G

题目意思:

  有n个人,每个人有一个礼物,每个人能拿自己礼物,n个人随机送礼物,给一个数字k,求出可以找到k个人,满足:这k个人里面,第一个人把礼物给第二个人,第二个人把礼物给第三个人,以此类推,第k个人把礼物给第1个人.求满足这个条件的概率.

组合数学:

满足条件的一组k个人称为一个k环,注意:可能有多个k环!先考虑至少形成一个k环的情况:A(n,k) * (n-1)^(n-k) / (k * (n-1)^(n)) == A(n, k) / (k * (n-1)^k) ;然后在考虑至少形成m个环的情况.

设至少形成m个环的概率是:f(m) = A(n, km)/(k^m*m!*(n-1)^(km)) 所以只需要递推m = 1 .... m = n/k

然后可以发现:f(m)/f(m-1) = A(n, km) / (A(n, k(m-1))*k*m(n-1)^k),因此可以根据计算出的f(1)求出f(2), f(3)....

根据容斥:ans = f(1) - f(2) + f(3) - f(4) + .... 

ORZ lyl的代码:

 1 #include <cstdlib>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <cctype>
 6 #include <algorithm>
 7 #include <queue>
 8 #include <set>
 9 #include <stack>
10 #include <map>
11 #include <list>
12 using namespace std;
13 #define INF 0x3f3f3f3f
14 const double eps=1e-9;
15 
16 int main ( int argc, char *argv[] )
17 {
18     double ans, t;
19     int i, k, n, tot, j;
20 #ifndef ONLINE_JUDGE
21     freopen("in.txt", "r",stdin);
22 #endif
23     while (~scanf("%d", &tot)) {
24         while (tot--) {
25             scanf("%d%d", &n, &k);
26             for (i=0,ans=1; i<k; ans=ans*(n-i++)/(n-1));
27             for (t=ans/=k,i=2; i*k<=n; ++i) {
28                 for (t=t/i/k,j=n-(i-1)*k; j>n-i*k; t=t*j--/(n-1));
29                 if (t<eps) break; else ans = i&1?ans+t:ans-t;
30             }
31             printf("%.9lf\n", ans);
32         }
33     }
34         return EXIT_SUCCESS;
35 }            

不经过深思熟虑是写不出这么精简的代码的...ORZ

posted on 2013-07-18 17:26  aries__liu  阅读(347)  评论(0编辑  收藏  举报