题目大意
给定一个
W
∗
H
W*H
W∗H的方格矩阵。和
n
n
n的小方格阵,每个覆盖了左下角为
x
i
1
,
y
i
1
x_{i1},y_{i1}
xi1,yi1,右上角为
x
i
2
,
y
i
2
x_{i2},y_{i2}
xi2,yi2的方格。
每一步会等概率随机选择一个方格阵(包括被染色的),把它全部涂黑,问把 W ∗ H W*H W∗H的方格矩阵全部涂黑的期望步数是多少?
思路
考虑一个简化模型, n n n个方块,每次等概率随机染黑一个,问期望步数是多少?
设 f [ i ] f[i] f[i]为已经染黑了 i i i个方格后,再全部染黑的期望步数。
那么已经染黑了
i
i
i个的情况下,有
n
−
i
n
\frac{n - i}{n}
nn−i的概率选择一个没有被染黑的,有
i
n
\frac{i}{n}
ni选择了一个已经被染黑的方块
即
f
[
i
]
=
1
+
n
−
i
n
∗
f
[
i
+
1
]
+
i
n
f
[
i
]
f[i] = 1+ \frac{n - i}{n}*f[i+1] + \frac{i}{n}f[i]
f[i]=1+nn−i∗f[i+1]+nif[i]
化简后
f
[
i
]
=
n
n
−
i
+
f
[
i
+
1
]
f[i] = \frac{n}{n-i} + f[i+1]
f[i]=n−in+f[i+1]
这个式子也可以感性理解,如果每次都能选到未被涂黑的格子,那么
f
[
i
]
=
1
+
f
[
i
+
1
]
f[i] = 1 + f[i+1]
f[i]=1+f[i+1],但事实我们未必每次都会选到,所以需要额外花费几次。
那么这一题也是这种思路,只不过此时的 i i i是状态压缩含义,表示选择了哪些方格阵。
f [ i ] = i . c o u n t n ∗ f [ i ] + 1 n ∑ f [ i < < ( 1 < < j ) ) ] f[i] = \frac{i.count}{n}*f[i] + \frac{1}{n}\sum f[i << (1<<j))] f[i]=ni.count∗f[i]+n1∑f[i<<(1<<j))]
其中 i . c o u n t i.count i.count是 i i i中 1 1 1的个数(即已经染过的方格阵), j j j是未被染黑的方格阵。
由于坐标较大,所以我们需要离散化点的坐标。
而且为了防止常数过大,用bitset代替了离散后的矩阵(每一位的 0 , 1 0,1 0,1)代表这个格子有没有被染色。
我们保存一下每一个方格阵会染色哪些方格,然后就dfs遍历所有状态。
代码
#include <cstdio>
#include <iostream>
#include <bitset>
#include <algorithm>
#include <cstring>
using namespace std;
const long long Mod = 998244353;
int T, n, W, H, sx[25], sy[25], cntx, cnty, k;
long long inv[25], f[1 << 11];
bitset<500> mask[12], state, tmp; //mask是每个方格阵能够染色哪些方格
struct Rect {
pair<int, int> l, r;
}rect[15];
inline long long qpow(long long a, long long b)
{
long long res = 1;
while(b) {
if(b & 1)
res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
void dfs(int u, int s)
{
if(u == n + 1) {
f[s] = state.count() == k ? 0 : -1;
return ;
}
bitset<500> t = state;
dfs(u + 1, s);
state = t;
state |= mask[u];
dfs(u + 1, s | (1 << (u - 1)));
}
int main()
{
for(int i = 1; i <= 20; ++i)
inv[i] = qpow(i, Mod - 2);;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
scanf("%d%d", &W, &H);
cntx = cnty = 0;
memset(f, 0, sizeof f);
for(int i = 1; i <= n; ++i) {
scanf("%d%d%d%d", &rect[i].l.first, &rect[i].l.second, &rect[i].r.first, &rect[i].r.second);
rect[i].l.first = min(rect[i].l.first, W); rect[i].l.second = min(rect[i].l.second, H);
rect[i].r.first = min(rect[i].r.first, W); rect[i].r.second = min(rect[i].r.second, H);
sx[++cntx] = rect[i].l.first; sx[++cntx] = rect[i].r.first;
sy[++cnty] = rect[i].l.second; sy[++cnty] = rect[i].r.second;
}
sort(sx + 1, sx + 1 + cntx); sort(sy + 1, sy + 1+ cnty);
cntx = unique(sx + 1, sx + 1 + cntx) - (sx + 1);
cnty = unique(sy + 1, sy + 1 + cnty) - (sy + 1);
k = (cntx - 1) * (cnty - 1);
for(int i = 1; i <= n; ++i) {
rect[i].l.first = lower_bound(sx + 1, sx + 1 + cntx, rect[i].l.first) - sx;
rect[i].l.second = lower_bound(sy + 1, sy + 1 + cnty, rect[i].l.second) - sy;
rect[i].r.first = lower_bound(sx + 1, sx + 1 + cntx, rect[i].r.first) - sx;
rect[i].r.second = lower_bound(sy + 1, sy + 1 + cnty, rect[i].r.second) - sy;
mask[i].reset(); tmp.reset();
tmp = (1 << (rect[i].r.second - rect[i].l.second)) - 1;
tmp <<= rect[i].l.second - 1;
for(int j = rect[i].l.first - 1; j < rect[i].r.first - 1; ++j)
mask[i] |= (tmp << (j * cnty));
}
state.reset();
dfs(1, 0);
if(f[(1 << n) - 1] == -1) {
printf("-1\n");
continue;
}
for(int i = (1 << n) - 1; i >= 0; --i) {
if(f[i] != -1)
continue;
int cnt = 0;
long long res = 0;
for(int j = 1; j <= n; ++j) {
if((i >> (j - 1)) & 1)
cnt++;
else
res = (res + 1LL * inv[n] * f[i | (1 << (j - 1))]) % Mod;
}
f[i] = (((1LL + res) * n) % Mod * inv[n - cnt]) % Mod;
}
printf("%lld\n", f[0]);
}
return 0;
}