题解:AT_abc434_d [ABC434D] Clouds

· · 题解

题目大意

天空中有 2000\times2000 的方格,和 N(1\le N\le 2\times10^5) 朵云,所有满足 U_i\le r\le D_iL_i\le c\le R_i 格子 (r,c) 被云朵 i 覆盖。输出

## 思路 首先考虑暴力做法,对于每朵云,将所覆盖方格上的云朵数量加 $1$,最终统计出每个方格上云朵的数量。接着枚举每个 $i$,将第 $i$ 朵云所覆盖的方格上的云朵数减 $1$,接着枚举每个方格,统计云朵数为 $0$ 的方格的数量。 ```cpp for (int i = 1; i <= n; i++) { int u,d,l,r; cin >> u >> d >> l >> r; m[i] = {u,d,l,r}; for (int j = u; j <= d; j++) for (int k = l; k <= r; k++) cnt[j][k]++; } for (int i = 1; i <= n; i++) { for (int j = m[i].u; j <= m[i].d; j++) for (int k = m[i].l; k <= m[i].r; k++) cnt[j][k]--; int num = 0; for (int j = 1; j <= 2000; j++) for (int k = 1; k <= 2000; k++) if (cnt[j][k] == 0) num++; cout << num << endl; for (int j = m[i].u; j <= m[i].d; j++) for (int k = m[i].l; k <= m[i].r; k++) cnt[j][k]++; } ``` 时间复杂度为 $O(NC)$,其中 $C$ 为方格数量,最大为 $2000\times2000$,显然会超时。 由于以上思路中存在许多统计数量的内容,可以使用差分前缀和进行优化:先用二维差分数组记录方格上云朵数量间的差值,接着通过前缀和得出每个方格上云朵的数量,将统计数量的时间复杂度降至 $O(N+C)$,接着枚举所有方格统计被云朵覆盖的方格数量 $num$。由于删除云朵后,只会影响到仅被该云朵覆盖的方格数,也就是被该云朵覆盖的方格中,被云朵覆盖数量为 $1$ 的方格数量。所以可以使用一个前缀和数组统计前面的方格中,被云朵覆盖数量为 $1$ 的方格数量,在枚举每朵云时,可在 $O(1)$ 的时间复杂度内求解出答案。此时将云朵 $i$ 删除后未被覆盖的云朵数量为 $2000\times2000-(num-k_i)$,$k_i$ 表示被云朵 $i$ 覆盖的方格中,被云朵覆盖数量为 $1$ 的方格数量。 ### 二维差分前缀和 设二位数组 $a$ 的前缀和数组为 $pa$,则 $pa_{i,j}=\sum_{r=1}^i\sum_{c=1}^ja_{r,c}$。根据容斥原理,可以得到:$a_{i,j}=pa_{i,j}-pa_{i-1,j}-pa_{i,j-1}+pa_{i-1,j-1}$。根据以上式子,可以得出差分数组 $da_{i,j}$ 的值为 $a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1}$。如果要将满足 $u\le x\le d$ 且 $l\le y\le r$ 的 $a_{x,y}$ 都增加 $1$,则根据求解 $da_{i,j}$ 的式子,可以发现只有 $da_{u,l}$ 和 $da_{d+1,r+1}$ 的值会加 $1$,$da_{u,r+1}$ 和 $da_{d+1,l}$ 的值会减 $1$,而其他值不变。 则在以上题目中需要使用到的式子有: 式子 A:$da_{i,j}=a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1}$。 式子 B:$a_{i,j}=da_{i,j}+a_{i-1,j}+a_{i,j-1}-a_{i-1,j-1}$。 式子 C:$pa_{i,j}=a_{i,j}+pa_{i-1,j}+pa_{i,j-1}-pa_{i-1,j-1}$。 式子 D:$a_{i,j}=pa_{i,j}-pa_{i-1,j}-pa_{i,j-1}+pa_{i-1,j-1}$。 式子 A,B 用于统计每个格子上云朵的数量,式子 C,D 用于统计相应格子内仅被 $1$ 朵云朵覆盖的格子的数量。 ## AC Code ```cpp #include <iostream> using namespace std; const int N = 2005; struct Cloud { int u,d,l,r; }c[200005]; int df[N][N]; //统计覆盖云朵数的差分数组 int cnt[N][N]; //覆盖云朵数 int pcnt1[N][N]; //统计仅被 1 朵云朵覆盖的方格数量的前缀和数组 int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { int u,d,l,r; cin >> u >> d >> l >> r; df[u][l]++; //套用式子 A df[u][r+1]--; df[d+1][l]--; df[d+1][r+1]++; c[i] = {u,d,l,r}; } for (int i = 1; i <= 2000; i++) for (int j = 1; j <= 2000; j++) cnt[i][j] = df[i][j]+cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]; //套用式子 B int num = 0; for (int i = 1; i <= 2000; i++) for (int j = 1; j <= 2000; j++) if (cnt[i][j] > 0) num++; for (int i = 1; i <= 2000; i++) for (int j = 1; j <= 2000; j++) pcnt1[i][j] = pcnt1[i-1][j]+pcnt1[i][j-1]-pcnt1[i-1][j-1]+(cnt[i][j] == 1); //套用式子 C for (int i = 1; i <= n; i++) { int k = pcnt1[c[i].d][c[i].r]-pcnt1[c[i].u-1][c[i].r]-pcnt1[c[i].d][c[i].l-1]+pcnt1[c[i].u-1][c[i].l-1]; //套用式子 D cout << 2000*2000-num+k << endl; } return 0; } ```