【ICPC】2022 昆明站 B题 题解

题目大意

给定一个 W ∗ H W*H WH的方格矩阵。和 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 WH的方格矩阵全部涂黑的期望步数是多少?

题目链接

思路

考虑一个简化模型, n n n个方块,每次等概率随机染黑一个,问期望步数是多少?

f [ i ] f[i] f[i]为已经染黑了 i i i个方格后,再全部染黑的期望步数。

那么已经染黑了 i i i个的情况下,有 n − i n \frac{n - i}{n} nni的概率选择一个没有被染黑的,有 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+nnif[i+1]+nif[i]
化简后 f [ i ] = n n − i + f [ i + 1 ] f[i] = \frac{n}{n-i} + f[i+1] f[i]=nin+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.countf[i]+n1f[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值