嘘~ 正在从服务器偷取页面 . . .

CF398B Painting The Wall 题解


CF398B Painting The Wall 题解

题目链接:CF398B Painting The Wall

题意

cxy 打算漆一堵墙。这个墙由 $n^2$ 块瓷砖构成,它们构成 $n \times n$ 的表。有些瓷砖是被涂上颜色的,还有些则没有。她想要将这堵墙漆得漂亮,她会按照以下四个步骤进行:

  1. 首先,cxy 会检查这堵墙,如果每行、每列均有涂上颜色的瓷砖,则停止刷墙,否则跳转到步骤 2。

  2. cxy 从这 $n^2$ 块瓷砖中均匀随机地选择一块瓷砖。

  3. 如果该瓷砖没有被涂色,则她将那块瓷砖涂色,否则,她什么都不用干。

  4. 然后,她休息 $1$ 分钟,然后回到步骤 1。

cxy 觉得,她会花费太多时间在砌墙上。所以,她想知道,她按照上面的方法砌墙所需的时间的期望。你可以假设涂一块瓷砖不用花费任何时间。

输入格式

第一行包含两个正整数 $n, m~(1 \leq n \leq 2000,~0 \leq m \leq \min \{n^2, 20000\})$,表示墙的大小和已经涂色的瓷砖的个数。

接下来的 $m$ 行,每行包含两个正整数 $r, c~(1 \leq r_i, c_i \leq n)$,表示被涂色的瓷砖的位置。保证这些位置互异,且行列均为 $1 \sim n$ 编号。

输出格式

输出一行一个实数,表示砌墙的期望时间。答案被认为正确当且仅当相对或绝对误差不超过 $10^{-4}$。

考虑概率dp。设 $f_{i,j}$ 表示还有 $i$ 行 $j$ 列没有涂颜色时剩下的时间的期望。

边界 $f_{0,0} = 0$ ,最后的答案即为 $f_{r,c}$ ,其中 $r,c$ 分别表示初始未染色的行数和列数。

考虑 $f_{i,j}$ 的转移。设 $f_{i,j}$ 的这些未染色的行和列为 $\mathcal{R}$ 和 $\mathcal{C}$ ,则他们把整个网格图分成了四个子集

  • 该行、该列均未上色的$S_1 = \left\{ (x, y) \mid x \in \mathcal{R}, ~y \in \mathcal{C} \right\}$
  • 行上色列未上色的 $S_2 = \left\{ (x, y) \mid x \notin \mathcal{R},~ y \in \mathcal{C} \right\}$
  • 列上色行未上色的 $S_3 = \left\{ (x, y) \mid x \in \mathcal{R}, ~y \notin \mathcal{C} \right\}$
  • 行列均已上色的$S_4 = \left\{ (x, y) \mid x \notin \mathcal{R}, ~y \notin \mathcal{C} \right\}$

考虑下一步随机选的格子 $k$ 。

  • 首先有 $\frac{\left|S_1\right|}{\left|S_1\right|+\left|S_2\right|+\left|S_3\right|+\left|S_4\right|}=\frac{i j}{n m}$ 的概率满足 $k \in S_1$

    此时未上色的行数变为 $i-1$ ,列数变为 $j-1$ ,剩下的期望时间为 $f_{i-1,j-1}$ 。

  • 类似的,有 $\frac{\left|S_2\right|}{\left|S_1\right|+\left|S_2\right|+\left|S_3\right|+\left|S_4\right|}=\frac{(n-i) j}{n m}$ 的概率满足 $k \in S_2$

    此时未上色的行数变为 $i$ ,列数变为 $j-1$ ,剩下的期望时间为 $f_{i,j-1}$ 。

  • ……(其他两种情况类似)

则转移方程为

然后解一下这个 $f_{i,j}$ 的一元一次方程,就可以得到 $f_{i,j}$ 的转移方程了。

时间复杂度 $\mathcal{O}(n^2 + m)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e3+15))

double f[N][N];
int n,m,R,C,r[N],c[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cout << fixed << setprecision(8);
    cin >> n >> m;
    for(int i=1,x,y; i<=m; i++)
    {
        cin >> x >> y;
        r[x] = c[y] = 1;
    }
    for(int i=1; i<=n; i++){ R += !r[i], C += !c[i]; }
    f[1][1] = 0; // 平移防止下标-1越界
    for(int i=0,x,y,z; i<=R; i++)
        for(int j=0; j<=C; j++)
        {
            if(!i && !j) continue;
            x = i * j; y = i * (n-j); z = (n-i) * j;
            f[i+1][j+1] = 
            (x*f[i][j] + y*f[i][j+1] + z*f[i+1][j] + (double)n*n) / (double)(x+y+z);
        }
    cout << f[R+1][C+1] << '\n';
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/?redirect=343


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录