Solution
场上的想法(显然是错的)是这样的: 我们假设棋子是一个一个地放置的, 考虑在放置棋子的过程中可能出现哪些状态. 我们令有序整数对\((i, j)\)表示总共控制了\(i\)行\(j\)列的情况, 我naive地认为一个状态要么不出现, 要么只出现一次. 于是用\(f[i][j]\)来表示出现的概率, 直接进行DP. 然后我用随机函数对拍, 发现是WA的...
考虑问题出现在了哪里: 一个状态实际上是可以出现多次的. 比如说我们考虑分别控制了两行两列的状态: 两行两列产生4个交点, 这4个交点中可以有2个, 3个, 4个棋子. 因此我们发现还要多记录一维, 表示用了多少个棋子.
我们用\(f[x][i][j]\)表示用了\(x\)个棋子, 控制了\(i\)行\(j\)列的状态的出现概率.
\[ \begin{aligned} f[x][i][j] = &f[x - 1][i - 1][j - 1] \times \frac{mn - (i - 1)m - (j - 1)n + (i - 1)(j - 1)}{mn - (x - 1)} \\ &+ f[x - 1][i - 1][j] \times \frac{j \times (n - (i - 1))}{mn - (x - 1)} \\ &+ f[x - 1][i][j - 1] \times \frac{i \times (m - (j - 1))}{mn - (x - 1)} \\ &+ f[x - 1][i][j] \times \frac{i \times j - (x - 1)}{mn - (x - 1)} \end{aligned} \] 考虑如何统计答\[ ans = \sum_x f[x][i][j] \times x \] 同时我们注意到这个式子还不完全是对的: \(f[x - 1][n][m]\)不能用于继续转移.因此我们在\(f[x][n][m]\)上直接减去\(f[x - 1][n][m]\)即可.
#include#include const int N = 50, M = 50;double f[N * M + 1][N + 1][M + 1];int main(){ int cs; scanf("%d", &cs); while(cs --) { int n, m; scanf("%d%d", &n, &m); memset(f, 0, sizeof(f)); f[0][0][0] = 1; for(int x = 1; x <= n * m; ++ x) { for(int i = 1; i <= n; ++ i) for(int j = 1; j <= m; ++ j) f[x][i][j] = f[x - 1][i - 1][j - 1] * (m * n - (i - 1) * m - (j - 1) * n + (i - 1) * (j - 1)) / (m * n - (x - 1)) + f[x - 1][i - 1][j] * (j * (n - (i - 1))) / (m * n - (x - 1)) + f[x - 1][i][j - 1] * (i * (m - (j - 1))) / (n * m - (x - 1)) + f[x - 1][i][j] * (i * j - (x - 1)) / (m * n - (x - 1)); f[x][n][m] -= f[x - 1][n][m]; } double ans = 0; for(int i = 1; i <= n * m; ++ i) ans += i * f[i][n][m]; printf("%.10lf\n", ans); }}