题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3625
题意:
有N个房间,里面放着这N个房间的钥匙,不一定是放着自己的
你可以摧毁K个房间房间拿到K把钥匙(第一个房间你不能摧毁),为你能打开这N个房间的机会有多大
解题思路:
第二类斯特林数的应用
排除第一个这个例外,我们现在要用K把钥匙打开N个房间
也就相当于要有一些循环在里面,就是一把钥匙打开一个门以后,拿到的钥匙又可以打开下一个门。。。,这就相当于一个环
那么如果我们这N把钥匙组成一个环就可以,我们最多可以组成K个环
这就是斯特林数,即str[n][k]表示将n个数字分成K部分,并且每个部分组成一个环的种数(具体详见具体数学中文版P218-219)
如果不考虑那个VIP,N个房间可以被最多K把钥匙打开的情况,实际上就是1..N的置换组成最多K个环的情况,
这个就是第一类strling数之和S1[n][1]+S1[n][2]+…+S1[n][k]。
在这些情况里面,如果钥匙1恰好锁在房间1里也是不行的,所以还要减去N-1个房间被最多K-1把钥匙打开的情况数。
PS:如果想在ACM上有所建树,《具体数学》是一定要看的。
下面上代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 21;
#define LL long long
LL str[maxn][maxn];
void init_strling()
{
str[1][1]=1;
for(int i=1;i<maxn;i++)
str[i][0]=0;
for(int i=2;i<maxn;i++)
{
for(int j=1;j<=i;j++)
{
str[i][j] = (i-1)*str[i-1][j]+str[i-1][j-1];
}
}
}
LL cal(int n,int k)
{
LL a1=0;
LL a2=0;
for(int i=1;i<=k;i++)
a1+=str[n][i];
for(int i=1;i<=k-1;i++)
a2+=str[n-1][i];
//printf("a1 %d a2 %d\n",a1,a2);
return a1-a2;
}
LL cal2(int n)
{
LL a1=1;
for(int i=1;i<=n;i++)
a1*=i;
return a1;
}
int main()
{
int t;
scanf("%d",&t);
init_strling();
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
LL a1=cal(n,k);
LL a2=cal2(n);
printf("%.4lf\n",a1*1.0/a2);
}
return 0;
}