题目链接: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