[USACO3.1]形成的区域(P6432)

日期:2020-05-31

一、题意分析

题目链接

  1. 任务:给出一张宽为\(A\)长为\(B\)的白纸(颜色为\(1\)),现要求将\(N\)个有颜色且不透明的长方形顺序放在白纸上,可能重叠。求放好后那张纸上呈现的每种颜色的面积。
  2. 输入:第一行三个正整数\(A \ B \ N\),分别表示白纸的长、白纸的宽、长方形的个数;第\(2\)到\(N+1\)行,每行五个整数\(llx \ lly \ urx \ ury \ color\),表示有一个左下角坐标为\((llx,lly)\)、右上角坐标为\((urx,ury)\)、颜色为\(color\)的长方形。
  3. 输出:放好长方形后,输出且只输出那张纸上可视颜色的汇总。每行输出两个正整数,该可视颜色和其总可视面积。输出时按照\(color\)升序输出。
  4. 数据范围(原题):\(1 \leq N \leq 10^3\),\(1 \leq A, B \leq 10^4\),\(1 \leq color \leq 2.5 \times 10^3\)。

二、算法分析

1. 暴力

我们知道,暴力的时间复杂度为\(O(A·B·N)\),即\(10^{11}\),故无法\(AC\)。

所以,我们在\(Excel\)上枚举暴力的过程,看看能否有所发现。

输入样例

20 20 3

2 2 18 18 2

0 8 19 19 3

8 0 10 19 4

0). 初始状态(红点为原点)

1). 放第一个长方形

2). 放第二个长方形

3). 放第三个长方形

4). 验证(用\(COUNTIF\)函数)

1 91
2 84
3 187
4 38

画图后,我们发现:其实填了大片相同的数字(颜色),如果我们只用一个数字来表示这个区域(小长方形)的话,那就会快很多。

2. 离散化

1). 扫描线

根据上面的想法,我们来绘制这三个长方形和黑色边框的白纸的扫描线。扫描线就是长方形的四边所在的直线(重叠的只画一条,因为有且只有一条):

那么,扫描线将这张白纸分为若干个长方形。我们只需要在处理每个长方形时,对涉及到的小长方形填上一个数字(代表一种颜色)即可,而非将每一个面积为\(1\)的最小单位填色。

2). 实现

初始化

扫描完毕后,将这张纸被切割成的每个小长方形填上\(1\),表示现在是一张白纸:

放第一个长方形

将第一个长方形涉及到的小长方形填上一个数字:

放第二个长方形

将第二个长方形涉及到的小长方形填上一个数字:

放第三个长方形

将第三个长方形涉及到的小长方形填上一个数字:

统计

一个可视颜色的总面积,就相当于所有和其颜色(数字)相等的被分割的小长方形的面积和。

三、程序框架

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int N = 1100;
const int N2 = 2200;
const int C = 2750;
const int AB = 11000; struct node{
int llx, lly, urx, ury, color;
};
node data[N]; //data存储每个长方形的信息
int map[N2][N2]; //map存储被分割(扫描)后的纸
//linex和liney存储扫描线
//rflcx和rflcy存储映射,即长方形的原始线对应的扫描线编号
//ans[i]表示第i号颜色的总可视面积
int linex[N2], liney[N2], rflcx[AB], rflcy[AB], ans[C];
int a, b, n, numx, numy, maxc; int main(){
//读入
scanf("%d%d%d", &a, &b, &n); //读入长方形 然后做扫描线
numx = numy = 0; maxc = 1;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d%d%d", &data[i].llx, &data[i].lly, &data[i].urx, &data[i].ury,&data[i].color); linex[++numx] = data[i].llx;
linex[++numx] = data[i].urx;
liney[++numy] = data[i].lly;
liney[++numy] = data[i].ury; if (maxc < data[i].color) maxc = data[i].color; //求最大颜色编号
} //处理边界(白纸的)扫描线
linex[++numx] = 0;
linex[++numx] = a;
liney[++numy] = 0;
liney[++numy] = b; //排序
sort(linex + 1, linex + numx + 1);
sort(liney + 1, liney + numy + 1); //去重
int tmp = numx; numx = 1;
for(int i = 2; i <= tmp; ++i)
if (linex[i] != linex[numx]) linex[++numx] = linex[i]; tmp = numy; numy = 1;
for(int i = 2; i <= tmp; ++i)
if (liney[i] != liney[numy]) liney[++numy] = liney[i]; //做映射
for (int i = 1; i <= numx; ++i) rflcx[linex[i]] = i;
for (int i = 1; i <= numy; ++i) rflcy[liney[i]] = i; //初始化:全部赋值为1,表示白纸
for (int i = 1; i < numx; ++i)
for (int j = 1; j < numy; ++j) map[i][j] = 1; //用长方形不断进行填色
for (int k = 1; k <= n; ++k)
for (int i = rflcx[data[k].llx]; i < rflcx[data[k].urx]; ++i)
for (int j = rflcy[data[k].lly]; j < rflcy[data[k].ury]; ++j) map[i][j] = data[k].color; //统计可视颜色面积
memset(ans, 0, sizeof ans);
for (int i = 1; i < numx; ++i)
for (int j = 1; j < numy; ++j) ans[map[i][j]] += (linex[i + 1] - linex[i]) * (liney[j + 1] - liney[j]); //输出答案
for (int i = 1; i <= maxc; ++i)
if (ans[i] > 0) printf("%d %d\n", i, ans[i]); return 0;
}

[USACO3.1]形成的区域(扫描线+离散化)的更多相关文章

  1. HDU - 1255 覆盖的面积(线段树求矩形面积交 扫描线+离散化)

    链接:线段树求矩形面积并 扫描线+离散化 1.给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. 2.看完线段树求矩形面积并 的方法后,再看这题,求的是矩形面积交,类同. 求面积时,用被覆 ...

  2. hdu1542 Atlantis (线段树+扫描线+离散化)

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  3. POJ-1151-Atlantis(线段树+扫描线+离散化)[矩形面积并]

    题意:求矩形面积并 分析:使用线段树+扫描线...因为坐标是浮点数的,因此还需要离散化! 把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用col表示该区间有多少个下边,sum代表该区 ...

  4. poj1151 Atlantis (线段树+扫描线+离散化)

    有点难,扫描线易懂,离散化然后线段树处理有点不太好理解. 因为这里是一个区间,所有在线段树中更新时,必须是一个长度大于1的区间才是有效的,比如[l,l]这是一根线段,而不是区间了. AC代码 #inc ...

  5. Helter Skelter (扫描线 + 离散化 + 树状数组)

    扫描线:按照其中一个区间的标记为pos,然后左区间标记d为正影响,有区间标记d为负影响,然后根据所有的pos排序.pos从小扫到大,那么对于某一个区间一定会被扫过2次,那么经过2次之后就只剩下中间那一 ...

  6. HDU1542 扫描线+离散化

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  7. hdu 4419 线段树 扫描线 离散化 矩形面积

    //离散化 + 扫描线 + 线段树 //这个线段树跟平常不太一样的地方在于记录了区间两个信息,len[i]表示颜色为i的被覆盖的长度为len[i], num[i]表示颜色i 『完全』覆盖了该区间几层. ...

  8. HDU1255 覆盖的面积 —— 求矩形交面积 线段树 + 扫描线 + 离散化

    题目链接:https://vjudge.net/problem/HDU-1255 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input输入数据的第一行是一个正整数T(1<= ...

  9. HDU 1542 Atlantis (线段树 + 扫描线 + 离散化)

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

随机推荐

  1. 其他函数-web_concurrent

    web_concurrent_start函数是并发组开始的标记.组中所有的函数是并发执行的,并发组的结束符为web_concurrent_end 函数. 在并发组中,可以包含的函数有: web_url ...

  2. Springboot打包放到Tomcat中报错 One or more listener fail to start

    1.问题: Springboot项目直接启动不报错,打war包放到外部容器Tomcat.东方通上,在@Weblistener注解的监听器类中报错 One or more listener fail t ...

  3. numpy中array数组对象的储存方式(n,1)和(n,)的区别

    资料:https://stackoverflow.com/questions/22053050/difference-between-numpy-array-shape-r-1-and-r 这篇文章是 ...

  4. 厉害了!除了find命令,还有这么多文件查找命令,高手必备!

    大家好,我是良许. 在系统里查找文件,是所有工程师都必备的技能(不管你用的是 Windows .Linux.还是 MacOS 系统).对于 Linux 操作系统,单单一个 find 命令就可以完成非常 ...

  5. java中int和Integer的区别?为什么有了int还要有设计Integer?

    参考https://blog.csdn.net/chenliguan/article/details/53888018 https://blog.csdn.net/myme95/article/det ...

  6. deepin双屏实现方式

    先xrandr --listproviders看下有几个provider,如果有多个,那么可能是不同显示口在不同显卡上,运行xrandr --setprovideroutputsource 0 1或x ...

  7. cc32b_demo-32dk2j_cpp_纯虚函数与抽象类2-txwtech

    cc32b_demo-32dk2j_cpp_纯虚函数与抽象类2-txwtech //纯虚函数是用来继承用的//纯虚函数//抽象类-抽象数据类型//*任何包含一个或者多个纯虚函数的类都是抽象类//*不要 ...

  8. Java Jar 包加密 -- XJar

    Java Jar 包加密 一.缘由 Java的 Jar包中的.class文件可以通过反汇编得到源码.这样一款应用的安全性就很难得到保证,别人只要得到你的应用,不需花费什么力气,就可以得到源码. 这时候 ...

  9. 5种经典的Linux桌面系统

    最近一直在准备Linux相关的PPT,对于一个老码农来说Linux系统自然是比较熟悉了,随口可以说出好几种Linux的版本,然而对于计算机初学者可能就知道windows操作系统.也许你告诉他你可以安装 ...

  10. Stream替代for-编码五分钟-划水五小时

    Stream替代for-编码五分钟-划水五小时 天空没有痕迹,风雨已在心中. 背景:使用Stream 流式操作取代俄罗斯式套娃的for循环,解放底层劳动密集型码畜的双手,使编码五分钟划水五小时,不再是 ...