P11215 【MX-J8-T3】水星湖
说明
说明
本系列由 Lee_King_Jimmy 推出,目的是将全网的题目都解一遍
这是第 444 篇
上一篇: 清空全网题目系列 · 洛谷 · P1054 [NOIP 2005 提高组] 等价表达式
下一篇: 未完待续
题目背景
原题链接:https://oier.team/problems/J8C。
题目描述
有一个 n×mn×mn×m 的矩形网格。用数对 (x,y)(x,y)(x,y) 表示第 xxx 行、第 yyy 列的位置。
网格内有 qqq 片湖泊(qqq 可能为 000),第 iii 片湖泊覆盖了左上角为 (ai,1,bi,1)(a_{i,1},b_{i,1})(ai,1,bi,1)、右下角为 (ai,2,bi,2)(a_{i,2} ,b_{i,2})(ai,2,bi,2) 的矩形区域,这片区域里的所有位置都被称为湖泊。如果一个位置不属于任何一片湖泊,它就是陆地。湖泊两两不会重叠,但可能相邻。小 Y 会进行 rrr 次种树。第 iii 次,他在第 tit_iti 秒向 (xi,yi)(x_i, y_i)(xi,yi) 里种下一棵树,保证该位置不为湖泊,且要么没有种下或生长过树,要么曾经种下或生长的树已经死亡。保证种树是按照时间顺序进行的,即 t1,t2,…,trt_1,t_2,…,t_rt1,t2,…,tr单调不降。
每一秒,对于每个位置 (x,y)(x,y)(x,y),若它同时满足如下所有条件,则会在 (x,y)(x,y)(x,y) 处生长出一棵树:
- 它是一块无树存活的陆地;
- 它与一块湖泊相邻;
- 它在前一秒与一棵存活的树相邻。
(上述所说的相邻是在四连通意义下的)
如果一棵树在存活大于 kkk 秒后(以其被种下或生长出来时开始计算),与其相邻的所有位置均为无树存活的陆地,则它会死亡。
小 Y 想要知道:经过充分多时间后(也即,经过足够多的时间,使得网格内不会有新的位置长出树,也不会有旧的树死去的状态下),网格内最终会有多少棵树。
输入格式
第一行,五个整数 n,m,q,r,kn,m,q,r,kn,m,q,r,k。
接下来 qqq 行,每行四个正整数 ai,1,bi,1,ai,2,bi,2a_{i,1},b_{i,1},a_{i,2}, b_{i,2}ai,1,bi,1,ai,2,bi,2,描述第 i 片湖泊的位置。保证湖泊两两不会重叠。
接下来 rrr 行,每行三个正整数 ti,xi,yit_i,x_i,y_iti,xi,yi,分别表示第 i 棵树被种下的秒数和行列位置。保证 t1,t2,…,trt_1,t_2,…,t_rt1,t2,…,tr单调不降。
输出格式
仅一行一个整数,表示经过充分多时间后,网格内最终会有多少棵树。
输入输出样例
输入 #1复制
5 6 2 1 1
2 1 3 3
3 5 5 6
1 1 5
输出 #1复制
10
输入 #2复制
3 3 0 3 1
1 3 1
2 1 1
3 2 1
输出 #2复制
2
说明/提示
【样例解释 #1】
如图所示,为经过充分多时间后网格中的情况。
网格内不会有新的位置长出树,也不会有旧的树死去,所以经过充分多时间后,网格内有 10 棵树。
【样例解释 #2】
在这一组数据中,所有位置都是陆地,没有湖泊。
第 1 秒时,第一棵树在 (3,1) 被种下。
第 2 秒时,第二棵树在 (1,1) 被种下。紧接着,第一棵树已存活 >1 秒,且与其相邻的所有位置均为没有存活的树的陆地,因此死亡。
第 3 秒时,第三棵树在 (2,1) 被种下。紧接着,第二棵树已存活 >1 秒,而此时第三棵树与其相邻,因此不死亡。
随后,网格内不会有新的位置长出树,也不会有旧的树死去。所以经过充分多时间后,网格内有 2 棵树。
【样例 #3】
见附件中的 lake/lake3.in 与 lake/lake3.ans。
该组样例满足测试点 4∼7 的约束条件。
【样例 #4】
见附件中的 lake/lake4.in 与 lake/lake4.ans。
该组样例满足测试点 8∼10 的约束条件。
【样例 #5】
见附件中的 lake/lake5.in 与 lake/lake5.ans。
该组样例满足测试点 13∼15 的约束条件。
【样例 #6】
见附件中的 lake/lake6.in 与 lake/lake6.ans。
该组样例满足测试点 16∼20 的约束条件。
思路
我们发现湖泊的大小才 300030003000,代表着我们可以使用一个二维数组存当前的状态
Step1: 标记湖泊
使用二维差分,学习链接: 传送门
差分之后再求一遍前缀和即可
Step2: 观察
我们可以观察一下这个题目,可以发现以下两点规律:
- 如果这个树在死亡之前可以产生一棵新的树,那么这棵树就一定不会死掉
- 如果这个树可以在长度为2的范围里面找到湖泊,那么这棵树可以产生更多的树,而且这个树不会死掉
对于结论1
因为题目中说了与其相邻的所有位置均为无树存活的陆地树就会死亡那么就代表,如果有两颗树是相连的那么这两颗树都不会死掉
对于结论2
因为长度为2,所以这个树的位置一定满足以下两个条件
- 和原来的这棵树的位置相连
- 和一块湖泊相连
这也不违背题目
Step3: 具体实现
对于每一颗树,我们将他直接标记在二维数组里面
然后遍历每一颗树;
- 如果这颗树满足结论 2, 就使用一个 dfs 标记出新长出来的数字
- 满足结论 1, 那么这个树一定也不会死掉
- 否则标记为 0, 表示死掉
代码
#include <stdio.h>#define reg register
#define FOR(i, a, b) for (reg int i = (a); i < (b); i++)
#define REP(i, a, b) for (reg int i = (a); i <= (b); i++)
#define bool int
#define true 1
#define false 0inline int isdigit(int x) {return (x >= '0' && x <= '9');}char buf[1 << 21] = {0}, *p1=buf, *p2=buf;
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
int _read() { char c; int ret = 0;while (!isdigit(c = gc()));do {ret = (ret << 3) + (ret << 1) + c - '0';} while (isdigit(c = gc()));return ret;
}
#undef gc
#define putchar putchar
void write(int x) {int p[12] = {0}, pcnt = 0;if (x < 0) x = -x, putchar('-');if (x == 0) pcnt++;while (x) p[pcnt++] = x % 10, x /= 10;while (pcnt--) putchar(p[pcnt] + '0');
}
#undef putchar#define N 3010
#define M 100010int Map[N][N]; // 题目中的数组
int n, m, q, r, k;// 12 个位置
int px[] = {0, 1, 0, -1, 1, 1, -1, -1, 2, -2, 0, 0};
int py[] = {1, 0, -1, 0, -1, 1, -1, 1, 0, 0, 2, -2};
typedef struct tree_t
{int x, y, t;
}tree;tree trees[M];void UpdateTime(){FOR(i, 0, r) Map[trees[i].x][trees[i].y] = trees[i].t;}
bool isValed(int x, int y) {return (x >= 1 && y >= 1 && x <= n && y <= m);}
bool Near_Lake(int x, int y) {FOR(i, 0, 4) {int tx = x + px[i], ty = y + py[i];if (isValed(tx, ty) && Map[tx][ty] == -1) return true; // 可以活下来}return false;
}
bool is_huo(int x, int y) { // 这棵树是否可以活下来// 1. 周围有树 && 这两个树的存活时间之差 <= k(他们可以相辅相成)FOR(i, 0, 4) {int tx = x + px[i], ty = y + py[i];if (isValed(tx, ty) && Map[tx][ty] > 0 && Map[tx][ty] - Map[x][y] <= k) return true;}
// printf("QwQ");return false;
}
bool Size2_Near_Lake(int x, int y) {FOR(i, 0, 12) {int tx = x + px[i], ty = y + py[i];if (isValed(tx, ty) && Map[tx][ty] == -1) return true;}return false;
}
void DFS(int x, int y) {FOR(i, 0, 4) {int tx = x + px[i], ty = y + py[i];if (isValed(tx, ty) && Map[tx][ty] == 0 && Near_Lake(tx, ty)) {Map[tx][ty] = 1;DFS(tx, ty);}}
}void pt_Map() {REP(i, 1, n) {REP(j, 1, m) printf("%2d ", Map[i][j]);putchar('\n');}
}int main()
{
// freopen("data.txt", "r", stdin);
// freopen("lake.in", "r", stdin);
// freopen("lake.out", "w", stdout);n = _read(), m = _read(), q = _read(), r = _read(), k = _read();FOR(i, 0, q) {int z1 = _read(), z2 = _read(), z3 = _read(), z4 = _read();Map[z1][z2]--, Map[z3 + 1][z2]++, Map[z1][z4 + 1]++, Map[z3 + 1][z4 + 1]--; // 将湖设为 -1}REP(i, 1, n) REP(j, 1, m) Map[i][j] += Map[i - 1][j] + Map[i][j - 1] - Map[i - 1][j - 1]; // 计算数组FOR(i, 0, r) trees[i].t = _read(), trees[i].x = _read(), trees[i].y = _read(); // 读入数据UpdateTime();
// pt_Map();FOR(i, 0, r) {if (Size2_Near_Lake(trees[i].x, trees[i].y)) // 可以长出新的树DFS(trees[i].x, trees[i].y);else if (!is_huo(trees[i].x, trees[i].y)) Map[trees[i].x][trees[i].y] = 0; // 死了
// printf("+++++++\n");
// pt_Map();}int res = 0;REP(i, 1, n) REP(j, 1, m) res += (Map[i][j] > 0);write(res);return 0;
}
时间复杂度分析
观察,里面有很多两层循环,所以时间复杂度至少为 O(nm)O(nm)O(nm)
大家可能会认为 dfs 函数的时间复杂度太大其实不是的,因为这里每个点只会遍历一遍,所以时间复杂度为 O(nm)O(nm)O(nm)(这是dfs的调用次数)
总时间复杂度为 O(q+nm+r+nm)O(q+nm+r+nm)O(q+nm+r+nm) 化简为 O(q+r+nm)O(q+r+nm)O(q+r+nm) 跑出了 143ms143ms143ms
(时限为2000ms+O2优化)