hihoCoder - 1075 開鎖魔法III
阿新 • • 發佈:2018-08-13
solution int space 數量 getc urn dig des namespace 個,選出 \(k\) 個,求每一堆都至少有一個被選出的概率。
Description
一日,崔克茜來到小馬鎮表演魔法。
其中有一個節目是開鎖咒:舞臺上有 \(n(n\le 300)\) 個盒子,每個盒子中有一把鑰匙,對於每個盒子而言有且僅有一把鑰匙能打開它。初始時,崔克茜將會隨機地選擇 \(k\) 個盒子用魔法將它們打開。崔克茜想知道最後所有盒子都被打開的概率,你能幫助她回答這個問題嗎?
Solution
這 gay 題。
把 \(i\) 連向 \(a_i\) ,就成了若幹簡單環。只要最開始打開一個環內任何一個點,就可以打開整個環。
令每個環大小為 \(size[i]\) ,數量為 \(cnt\) ,那麽問題就變成了,\(cnt\) 堆物品,每堆有 \(size[i]\)
然後就可以 dp
了。\(dp[i][j]\) 表示前 \(i\) 堆,選了 \(j\) 個。轉移見代碼。
#include<bits/stdc++.h> using namespace std; template <class T> void read(T &x) { x = 0; bool flag = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == 45) flag = 1; for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; if (flag) x = -x; } #define N 301 #define rep(i, a, b) for (int i = (a); i <= (b); i++ ) int a[N], siz[N]; double f[N][N], c[N][N]; bool vis[N]; int main() { int T; read(T); rep(i, 0, 300) { c[i][0] = c[i][i] = 1; rep(j, 1, i - 1) c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; } while (T--) { int n, K, cnt = 0; read(n), read(K); rep(i, 1, n) read(a[i]); memset(vis, 0, sizeof vis); rep(i, 1, n) if (!vis[i]) { int j = i, sum = 0; while (!vis[j]) sum++, vis[j] = 1, j = a[j]; siz[cnt++] = sum; } memset(f, 0, sizeof f), f[0][0] = 1; rep(i, 0, cnt - 1) rep(j, 0, K) if (fabs(f[i][j]) > 1e-8) rep(k, 1, min(siz[i], K - j)) f[i + 1][j + k] += f[i][j] * c[siz[i]][k]; printf("%.5lf\n", f[cnt][K] / c[n][K]); } return 0; }
hihoCoder - 1075 開鎖魔法III