2021昆明B

容斥 + 矩形面积并 + 状压dp

B-Blocks_第46届ICPC亚洲区域赛(昆明)(正式赛) (nowcoder.com)

题意

给出一个矩形A \((0,0),(W,H)\), 给出 \(n\;(1<=n<=10)\) 个矩形 \((x_1,y_1),(x_2,y_2)\) (坐标分别为左下角,右上角)

每次在这 n 个矩形中等概率选择 1 个,给这个区域涂色,求能将 A 区域完全涂满的期望次数

思路

  1. 看数据范围可知可能是状压dp,且因为是期望dp,倒推

  2. 设状态 \(s\) 为已经涂过色的区域,如 101 表示第 0,2 个区域被涂过了

    \(f[s]\) 表示从 \(s\) 开始,还要期望几次能涂满 A

    转移:枚举 \(s\) 中 0 的位置 i,并且记录共有 cnt 个

    \(now+=f[s|2^i]\)

    \(f[s]=\frac {1}n*now+\frac {n-cnt}n*f[s]\)

  3. 现在的问题是求出 dp 的起点,即有哪些状态是已经把 A 涂满的,记为 \(f[s]=0\)

    求若干矩形的面积并是否能覆盖 A,但并集不好处理,可以先求出 \(And[s]\) 表示 \(s\) 集合的面积交,用容斥求出面积并 \(Or[s]\)

    通过 \(And[s]\)\(Or[s]\) 的过程可以枚举子集,\(O(3^n)\)

代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;
const int N = 10;
const int mod = 998244353;
int n;
struct Node
{
    ll x1, y1;
    ll x2, y2;
}a[N];

ll inv[N];
ll f[1 << N];
ll And[1 << N], Or[1 << N];
ll H, W;

ll qmi(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if (b & 1)
            ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans % mod;
}

void presolve()
{
    for (int i = 1; i <= n; i++)
        inv[i] = qmi(i, mod - 2);
    for (int s = 0; s < 1 << n; s++)
    {
        ll X1 = 0, Y1 = 0, X2 = W, Y2 = H;
        for (int i = 0; i < n; i++)
        {
            if (!(s >> i & 1))
                continue;
            auto [x1, y1, x2, y2] = a[i];
            X1 = max(X1, x1), Y1 = max(Y1, y1);
            X2 = min(X2, x2), Y2 = min(Y2, y2);
        }
        And[s] = max(0ll, X2 - X1) * max(0ll, Y2 - Y1);
    }
    for (int s = 0; s < 1 << n; s++)
    {
        Or[s] = 0;
        for (int ns = s; ns; ns = (ns - 1) & s)
            Or[s] += And[ns] * (__builtin_parity(ns) ? 1 : -1);
    }
}
    
void solve()
{
    ll tot = H * W;
    if (Or[(1 << n) - 1] < tot)
    {
        cout << -1 << endl;
        return;
    }
    for (int s = (1 << n) - 1; s >= 0; s--)
    {
        if (Or[s] == tot)
        {
            f[s] = 0;
            continue;
        }
        ll now = n, cnt = 0;
        for (int i = 0; i < n; i++)
        {
            if (s >> i & 1)
                continue;
            now = (now + f[s | (1 << i)]) % mod;
            cnt++;
        }
        f[s] = now * inv[cnt] % mod;
    }
    cout << f[0] << endl;
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        cin >> H >> W;
        for (int i = 0; i < n; i++)
            cin >> a[i].x1 >> a[i].y1 >> a[i].x2 >> a[i].y2;
        presolve();
        solve();
    }
    return 0;
}
posted @ 2022-10-20 12:10  hzy0227  阅读(32)  评论(0编辑  收藏  举报