hihocoder1075【开锁魔法】

hihocoder1075【开锁魔法】
题意是给你一个 \(1~n\) 的置换,求选 \(k\) 个可以遍历所有点的概率。
题目可以换个模型:有 \(n\) 个球,有 \(cnt\) 种不同的颜色,求选出 \(k\) 个球包含所有颜色的概率。
根据概率的定义,我们只需求出合法的方案数即可。
\(f[i][j]\) 表示选 \(j\) 个球,包含前 \(i\) 种颜色的方案数。转移枚举第 \(i\) 种颜色选的个数即可。
根据乘法原理和加法原理可得:

\[f[i+1][j+use]+=f[i][j]*c[a[i+1]][use] \]

初始条件是 \(f[0][0]=1\),结果是 \(f[cnt][k]/c[n][k]\)

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
	RG int ret; RG bool flag; RG char ch;
	ret=0, flag=true, ch=getchar();
	while (ch < '0' || ch > '9')
		ch == '-' ? flag=false : 0, ch=getchar();
	while (ch >= '0' && ch <= '9')
		ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
	return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 305, inf = 1<<30;

db c[N][N],f[N][N];
int to[N],a[N];
bool vis[N];

int main()
{
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	int T,n,m,i,j,p,cnt;
	for (i=1; i<N; ++i)
		{
			c[i][0]=c[i][i]=1;
			for (j=1; j<i; ++j)
				c[i][j]=c[i-1][j-1]+c[i-1][j];
		}
	T=gi();
	while (T--)
		{
			n=gi(), m=gi(), cnt=0;
			for (i=1; i<=n; ++i)
				{
					vis[i]=false, a[i]=0;
					for (j=0; j<=m; ++j)
						f[i][j]=0;
				}
			for (i=1; i<=n; ++i)
				to[i]=gi();
			for (i=1; i<=n; ++i)
				{
					if (vis[i])
						continue;
					p=i, cnt++;
					while (!vis[p])
						a[cnt]++, vis[p]=true, p=to[p];
				}
			if (m < cnt)
				{
					puts("0.00000000000");
					continue;
				}
			f[0][0]=1;
			for (i=0; i<=cnt; ++i)
				for (j=0; j<m; ++j)
					{
						if (f[i][j] == 0)
							continue;
						for (p=1; p<=a[i+1] && p+j <= m; ++p)
							f[i+1][j+p]+=f[i][j]*c[a[i+1]][p];
					}
			printf("%.9f\n",f[cnt][m]/c[n][m]);
		}
	return 0;
}
posted @ 2017-09-14 22:56  tbhkoymiads  阅读(117)  评论(0编辑  收藏  举报