并 (杭电多校1 1012)

进入博客阅读体验更佳:并 (杭电多校1 1012) | 付诺の小站 (funuo0728.github.io)

题目大意


平面直角坐标系上有 n ( 1 ≤ n ≤ 2 × 1 0 3 ) n(1 \le n \le 2 \times 10^3) n(1n2×103)个矩形,对于 k ∈ [ 1 , n ] k\in[1,n] k[1,n],求解在 n n n个矩形中随机选取 k k k个不同的矩形,其所有覆盖部分的并集的面积的期望值,由于答案可能很大,请对 998244353 998244353 998244353取模。

解题思路


官方题解的做法是用任选的方案数减去没有被覆盖的方案数,思路比较清晰简单。奈何笔者赛时并没有想到,一直在用容斥的思想去写,所幸最后还是写了出来。笔者认为还算是一次较为有益的尝试,所以这篇博客主要探究容斥的做法,至于此题具体做法将不再赘述。

考虑从 n n n个矩形里面取出 i i i个矩形,然后参考这样一个经典的图,

![外链图片转存失败,源站可能有防盗链机制,建3)

可以将 A A A B B B C C C分别看作一个矩形所覆盖的区域,它们之间有部分相互重叠,并假设当前 n n n 3 3 3,要从这三个矩形里面取 i i i个矩形,并求它们覆盖面积的期望。

首先参考这张图里的容斥公式:

∣ A ∪ B ∪ C ∣ = ∣ A ∣ + ∣ B ∣ + ∣ C ∣ − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ + ∣ A ∩ B ∩ C ∣ \vert A\cup B\cup C \vert = \vert A\vert + \vert B\vert +\vert C\vert - \vert A\cap B| - \vert A\cap C\vert - \vert B\cap C\vert + \vert A\cap B\cap C\vert ABC=A+B+CABACBC+ABC

假设被这三个矩形覆盖 1 1 1 2 2 2 3 3 3次的面积分别为 S 1 S_1 S1 S 2 S_2 S2 S 3 S_3 S3,那么上面的容斥就可以用另一种形式来表现:

C 1 1 S 1 C 3 − 1 i − 1 + C 2 1 S 2 C 3 − 1 i − 1 + C 3 1 S 3 C 3 − 1 i − 1 − C 2 2 S 2 C 3 − 2 i − 2 − C 3 2 S 3 C 3 − 2 i − 2 + C 3 3 S 3 C 3 − 3 i − 3 C_{1}^{1}S_1C_{3 - 1}^{i - 1} + C_{2}^{1}S_2C_{3 - 1}^{i - 1} + C_{3}^{1}S_3C_{3 - 1}^{i - 1} - C_{2}^{2}S_2C_{3 - 2}^{i - 2} - C_{3}^{2}S_3C_{3 - 2}^{i - 2} + C_{3}^{3}S_3C_{3 - 3}^{i - 3} C11S1C31i1+C21S2C31i1+C31S3C31i1C22S2C32i2C32S3C32i2+C33S3C33i3

对于 C 1 1 S 1 C 3 − 1 i − 1 + C 2 1 S 2 C 3 − 1 i − 1 + C 3 1 S 3 C 3 − 1 i − 1 C_{1}^{1}S_1C_{3 - 1}^{i - 1} + C_{2}^{1}S_2C_{3 - 1}^{i - 1} + C_{3}^{1}S_3C_{3 - 1}^{i - 1} C11S1C31i1+C21S2C31i1+C31S3C31i1这一块可以看作是计算任选一块矩形,其所覆盖的区域对答案的贡献, C 1 1 S 1 C_{1}^{1}S_1 C11S1表示从仅被一个矩形覆盖的区域上选择覆盖了这一块区域的其中一块矩形的方案数, C 2 1 S 2 C_{2}^{1}S_2 C21S2表示从仅被两个矩形覆盖的区域上选择覆盖了这一块区域的其中一块矩形的方案数, C 3 1 S 3 C_{3}^{1}S_3 C31S3也是类似的道理。至于 C 3 − 1 i − 1 C_{3 - 1}^{i - 1} C31i1,因为总共要选 i i i个矩形,现在已经确定了一个,那么还要从剩下的矩形里再选 i − 1 i - 1 i1个。

然后,就会发现这一块 C 1 1 S 1 C 3 − 1 i − 1 + C 2 1 S 2 C 3 − 1 i − 1 + C 3 1 S 3 C 3 − 1 i − 1 C_{1}^{1}S_1C_{3 - 1}^{i - 1} + C_{2}^{1}S_2C_{3 - 1}^{i - 1} + C_{3}^{1}S_3C_{3 - 1}^{i - 1} C11S1C31i1+C21S2C31i1+C31S3C31i1与容斥中 ∣ A ∣ + ∣ B ∣ + ∣ C ∣ \vert A\vert + \vert B\vert +\vert C\vert A+B+C这一块的意义是等价的

这一块 ( − C 2 2 S 2 C 3 − 2 i − 2 − C 3 2 S 3 C 3 − 2 i − 2 ) (- C_{2}^{2}S_2C_{3 - 2}^{i - 2} - C_{3}^{2}S_3C_{3 - 2}^{i - 2}) (C22S2C32i2C32S3C32i2)则等价于 ( − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ ) (- \vert A\cap B| - \vert A\cap C\vert - \vert B\cap C\vert) (ABACBC)

剩下的 C 3 3 S 3 C 3 − 3 i − 3 C_{3}^{3}S_3C_{3 - 3}^{i - 3} C33S3C33i3则等价于 ∣ A ∩ B ∩ C ∣ \vert A\cap B\cap C\vert ABC

那么我们就可以通过容斥的这种思想推广出这样的式子:

从 n 个矩形中任取 i 个矩形,它们交的总和 = ∑ j = 1 i ( − 1 ) j C n − j i − j ∑ k = j n C k j S k 从n个矩形中任取i个矩形,它们交的总和 = \sum_{j = 1}^{i}(-1)^{j}C_{n - j}^{i - j}\sum_{k = j}^{n}C_{k}^{j}S_k n个矩形中任取i个矩形,它们交的总和=j=1i(1)jCnjijk=jnCkjSk

最终求期望的公式便为:

r e s i = ∑ j = 1 i ( − 1 ) j C n − j i − j ∑ k = j n C k j S k C n i res_i = \frac{\sum_{j = 1}^{i}(-1)^{j}C_{n - j}^{i - j}\sum_{k = j}^{n}C_{k}^{j}S_k}{C_{n}^{i}} resi=Cnij=1i(1)jCnjijk=jnCkjSk

由于还需要从 1 1 1 n n n遍历 i i i的取值再加上快速幂求逆元,看起来时间复杂度是 O ( n 3 l o g n ) O(n^3logn) O(n3logn),但实际上通过观察发现, ∑ k = j n C k j S k \sum_{k = j}^{n}C_{k}^{j}S_k k=jnCkjSk这一块的值是固定的,所以可以先预处理,最终时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

笔者的数学功底较为薄弱,对于想法的表述实在是只能做到这样。如果你对这种做法有什么独到的见解或看法的话,欢迎给出意见或指导,笔者会尝试着继续努力的。

参考代码

#include <bits/stdc++.h>
#define maxn 4010
#define int long long
using namespace std;
const double eps = 1e-8;
const int mod = 998244353;
int s[maxn][maxn], C[maxn][maxn];
int x1_[maxn], y1_[maxn], x2_[maxn], y2_[maxn];
int nx[maxn], ny[maxn];
int tt[maxn], res[maxn];

int qmi(int a, int b, int m) {
    int res = 1;  a %= m;
    while(b) {
        if(b & 1)  res = res * a % m;
        a = a * a % m;
        b >>= 1;
    }
    return res;
}

void solve() {
    map<int, int>xs, ys;
    set<int> numx, numy;
    int n;  cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> x1_[i] >> y1_[i] >> x2_[i] >> y2_[i];
        numx.insert(x1_[i]);  numy.insert(y1_[i]);
        numx.insert(x2_[i]);  numy.insert(y2_[i]);
    }
    int curx = 0, cury = 0;

    for (auto u : numx)  {
        xs[u] = ++curx;
        nx[curx] = u;
    }
    for (auto u : numy) {
        ys[u] = ++cury;
        ny[cury] = u;
    }
    for (int i = 1; i <= n; ++i) {
        s[xs[x1_[i]]][ys[y1_[i]]]++;
        s[xs[x2_[i]]][ys[y1_[i]]]--;
        s[xs[x1_[i]]][ys[y2_[i]]]--;
        s[xs[x2_[i]]][ys[y2_[i]]]++;
    }

    for(int i = 1; i < curx; i++) {
        for(int j = 1; j < cury; j++) {
            int dx = nx[i + 1] - nx[i];
            int dy = ny[j + 1] - ny[j];
            s[i][j] = s[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
            tt[s[i][j]] = (tt[s[i][j]] + dx * dy % mod) % mod;
        }
    }

    for (int i = 1; i <= n; ++i) {
        for (int j = i; j <= n; ++j) {
            res[i] = (res[i] + C[j][i] * tt[j] % mod) % mod;
        }
    }
    for (int i = 1; i <= n; ++i) {
        int ans = 0;
        for (int j = 1; j <= i; ++j) {
            int tmp = C[n - j][i - j];
            if(j & 1)  ans = (ans + tmp * res[j] % mod) % mod;
            else  ans = ((ans - tmp * res[j] % mod) % mod + mod) % mod;
        }
        ans = ans * qmi(C[n][i], mod - 2, mod) % mod;
        cout << ans << '\n';
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);  cout.tie(0);
    for (int i = 0; i <= 2000; ++i) {
        for (int j = 0; j <= i; ++j) {
            if(j == 0 || j == i)  C[i][j] = 1;
            else  C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
    int t = 1;
    while (t--) {
        solve();
    }
    return 0;
}
  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值