进入博客阅读体验更佳:并 (杭电多校1 1012) | 付诺の小站 (funuo0728.github.io)
并
题目大意
平面直角坐标系上有 n ( 1 ≤ n ≤ 2 × 1 0 3 ) n(1 \le n \le 2 \times 10^3) n(1≤n≤2×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 ∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣
假设被这三个矩形覆盖 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} C11S1C3−1i−1+C21S2C3−1i−1+C31S3C3−1i−1−C22S2C3−2i−2−C32S3C3−2i−2+C33S3C3−3i−3
对于 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} C11S1C3−1i−1+C21S2C3−1i−1+C31S3C3−1i−1这一块可以看作是计算任选一块矩形,其所覆盖的区域对答案的贡献, 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} C3−1i−1,因为总共要选 i i i个矩形,现在已经确定了一个,那么还要从剩下的矩形里再选 i − 1 i - 1 i−1个。
然后,就会发现这一块 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} C11S1C3−1i−1+C21S2C3−1i−1+C31S3C3−1i−1与容斥中 ∣ 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}) (−C22S2C3−2i−2−C32S3C3−2i−2)则等价于 ( − ∣ A ∩ B ∣ − ∣ A ∩ C ∣ − ∣ B ∩ C ∣ ) (- \vert A\cap B| - \vert A\cap C\vert - \vert B\cap C\vert) (−∣A∩B∣−∣A∩C∣−∣B∩C∣)
剩下的 C 3 3 S 3 C 3 − 3 i − 3 C_{3}^{3}S_3C_{3 - 3}^{i - 3} C33S3C3−3i−3则等价于 ∣ A ∩ B ∩ C ∣ \vert A\cap B\cap C\vert ∣A∩B∩C∣
那么我们就可以通过容斥的这种思想推广出这样的式子:
从 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)jCn−ji−j∑k=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=Cni∑j=1i(−1)jCn−ji−j∑k=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;
}