题面:传送门
题意:有N个矩形,按顺序向上覆盖,给你每个矩形左下角顶点的坐标与右上角顶点的坐标。求矩形覆盖后的周长(具体定义见下图)。
这道题是我在看2528漂浮法题解的时候看到的,博主整理了一下漂浮法的题目,其中就包括这道1177。既然都说了是漂浮法,那就直接上啊(开始的时候是这么想的,但是很快就发现事情并没有那么简单)。
最开始,我想对整体矩形进行操作,即根据两个顶点判断他们的交并关系。但是仔细分下来,无交集有4种情况,全覆盖(包含)有1种情况,部分相交有15种情况。这15种情况,按照漂浮法“不相交部分裂开继续向上漂”的思想,又要分成裂成2块、3块、4块、5块分别讨论。这个分类讨论实现起来非常棘手。终于写好之后,又发现这种方法漏下了仅有一边相交的情况——写到这里就该反应过来,不可以继续了。因为即使修复了这个Bug,很大可能还有漏掉的情况。这样下去是没有尽头的ORZ而且漂浮法的优势本就在于它大大简化了线段树繁琐的代码,写的时候我要吐了(还不如线段树有模板舒服)。
这个时候就又需要强大的队友的大脑 来拯救我了。队友听我讲了2528用线段树的思路之后,发现了漂浮法的本质:把矩形分成四根线分别向上漂!于是又回到了2528最基本的思路~我们在无交集、全覆盖之后的相交情况,针对横线/竖线只需要讨论三种情况:盖住左边,盖住右边,盖住中间(这种情况包含了边重合)。
好不容易写掉之后,终于如愿以偿地AC了 TLE了!吐了。这道题最难受的地方就是,全网都搜不到漂浮法的题解。只有一位聚聚博客里提到这题可以用漂浮法。我觉得我们的基本思路——拆成线向上漂——应该是没问题的了,聚聚能AC估计就是有哪些地方巧妙地优化了。不过想破头也想不出了……
短暂的三天漂浮法学习之后,坚定了我学好线段树的意念(。
总结一下这道题出现的问题:
- 分类讨论不要漏掉特殊情况,比如这种覆盖,想想边重合,多个全覆盖嵌套的情况
- 样例要仔细推,矩形套矩形的情况,内部矩形的周长是不算的(而如果直接漂浮,这个小矩形之上恰好没有覆盖它的矩形的话,我们就会算多一部分),最后debug的时候才发现这个问题。其实读题认真推下样例就可以避免这一点。
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAX_N 5010 //矩形个数
#define ll long long
struct rectangle
{
int x1, x2, y1, y2, mark;//x1y1左下角 x2y2右上角
}rec[MAX_N];
int N;
ll ans;
void linecheck(int x1, int y1, int x2, int y2, int i, int pos)//pos=1竖线 0横线
{
if (pos)//竖线
{
int m = i - 1;
while (i < N && (rec[i].y1 >= y2 || rec[i].y2 <= y1 || rec[i].x2 < x1 || rec[i].x1 > x1))
{
if (rec[i].x2 <= rec[m].x2 && rec[i].y2 <= rec[m].y2 && rec[i].y1 >= rec[m].y1 && rec[i].x1 >= rec[m].x1)
{
if (rec[i].mark) continue;
ans -= (rec[i].x2 - rec[i].x1) * 2 + (rec[i].y2 - rec[i].y1) * 2;
rec[i].mark = 1;
}
i++;
}
if (i >= N)
{
ans += y2 - y1;
return;
}
if (rec[i].y2 >= y2 && rec[i].y1 <= y1)
{
ans -= y2 - y1;
return;
}
if (rec[i].y1 > y1 && rec[i].y2 > y2)
{
linecheck(x1, y1, x2, rec[i].y1, i+1, 1);
ans -= y2 - rec[i].y1;
return;
}
if (rec[i].y2 < y2 && rec[i].y1 < y1)
{
linecheck(x1, rec[i].y2, x2, y2, i+1, 1);
ans -= rec[i].y2 - y1;
return;
}
if (rec[i].y2 < y2 && rec[i].y1 > y1)
{
linecheck(x1, rec[i].y2, x2, y2, i + 1, 1);
linecheck(x1, y1, x2, rec[i].y1, i + 1, 1);
ans -= rec[i].y2 - rec[i].y1;
return;
}
}
else//横线
{
while (i < N && (rec[i].x2 <= x1 || rec[i].x1 >= x2 || rec[i].y1 > y1 || rec[i].y2 < y1) )
i++;
if (i >= N)
{
ans += x2 - x1;
return;
}
if (rec[i].x1 <= x1 && rec[i].x2 >= x2)
{
ans -= x2 - x1;
return;
}
if (rec[i].x2 < x2 && rec[i].x1 < x1)
{
linecheck(rec[i].x2, y1, x2, y2, i + 1, 0);
ans -= rec[i].x2 - x1;
return;
}
if (rec[i].x1 > x1 && rec[i].x2 > x2)
{
linecheck(x1, y1, rec[i].x1, y2, i + 1, 0);
ans -= x2 - rec[i].x1;
return;
}
if (rec[i].x1 > x1 && rec[i].x2 < x2)
{
linecheck(x1, y1, rec[i].x1, y2, i + 1, 0);
linecheck(rec[i].x2, y1, x2, y2, i + 1, 0);
ans -= rec[i].x2 - rec[i].x1;
return;
}
}
}
int main()
{
scanf("%d", &N);
for (int i = 0; i < N; i++)
{
scanf("%d %d %d %d", &rec[i].x1, &rec[i].y1, &rec[i].x2, &rec[i].y2);//读入
rec[i].mark = 0;
}
for (int i = 0; i < N - 1; i++)
{
linecheck(rec[i].x1, rec[i].y1, rec[i].x1, rec[i].y2, i + 1, 1);
linecheck(rec[i].x2, rec[i].y1, rec[i].x2, rec[i].y2, i + 1, 1);
linecheck(rec[i].x1, rec[i].y1, rec[i].x2, rec[i].y1, i + 1, 0);
linecheck(rec[i].x1, rec[i].y2, rec[i].x2, rec[i].y2, i + 1, 0);
}
ans += (rec[N - 1].x2 - rec[N - 1].x1) * 2 + (rec[N-1].y2 - rec[N-1].y1) * 2;
printf("%lld\n", ans);
return 0;
}