POJ Mayor's posters——线段树+离散化
原文:http://blog.163.com/cuiqiongjie@126/blog/static/85642734201261151553308/
大致题意:
有一面墙,被等分为1QW份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到。)
解题思路:
水题,区间压缩映射(离散化)+ 线段树
首先建立模型:
给定一条数轴,长度为1QW,然后在数轴上的某些区间染色,第i次对区间染色为i,共染色N次。给出每次染色的区间,问最后能看见多少种颜色。
常规思路:
若第i次在区间[ai , bi]染色,则把[ai , bi]的每一格都染色为i。后染的颜色覆盖先染的颜色。由于染色N次,定义一个标记数组tagcol,从数轴第一格开始检查,一直检查到最后,出现过得颜色则记录到tagcol,最后统计tagcol中不同颜色的个数,就是所求。
数据规模太大,必定TLE。
应该用线段树去求解,这题只是线段树的入门水题,不懂线段树的同学去找一些相关资料大概学习一下。
然后我这里补充几点线段树的知识,网上关于线段树的资料很多有误导。
1、 线段树是二叉树,且必定是平衡二叉树,但不一定是完全二叉树。
2、 对于区间[a,b],令mid=(a+b)/2,则其左子树为[a,mid],右子树为[mid+1,b],当a==b时,该区间为线段树的叶子,无需继续往下划分。
3、 线段树虽然不是完全二叉树,但是可以用完全二叉树的方式去构造并存储它,只是最后一层可能存在某些叶子与叶子之间出现“空叶子”,这个无需理会,同样给空叶子按顺序编号,在遍历线段树时当判断到a==b时就认为到了叶子,“空叶子”永远也不会遍历到。
4、 之所以要用完全二叉树的方式去存储线段树,是为了提高在插入线段和搜索时的效率。用p*2,p*2+1的索引方式检索p的左右子树要比指针快得多。
5、 线段树的精髓是,能不往下搜索,就不要往下搜索,尽可能利用子树的根的信息去获取整棵子树的信息。如果在插入线段或检索特征值时,每次都非要搜索到叶子,还不如直接建一棵普通树更来得方便。
但是这题单纯用线段树去求解一样不会AC,原因是建立一棵[1,1QW]的线段树,其根系是非常庞大的,TLE和MLE是铁定的了。所以必须离散化。
通俗点说,离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:
有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。
现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9
然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9
对其升序排序,得2 3 4 6 8 9 10
然后建立映射
2 3 4 6 8 9 10
↓ ↓ ↓ ↓ ↓ ↓ ↓
1 2 3 4 5 6 7
那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。
离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。
附:海报张数上限为10000,即其端点映射的新数轴长度最多为20000。因此建立长度为1QW的离散数组dis时,可以使用unsigned short类型,其映射值最多为20000,这样可以节约空间开销。
//Memory Time
//21276K 547MS #include<iostream>
#include<algorithm>
using namespace std; class LineTree_Node
{
public: int s,e; //区间端点 int col; //区间颜色 LineTree_Node():s(0),e(0),col(0){}
}; class solve
{
public: solve(int n):N(n) { Initial(); Input(); CreatLineTree(1,Maxp,1); Solution(); } ~solve() { for(int i=1;i<=N;i++) delete[] reg[i]; delete[] ep; delete[] dis; delete[] tagcol; delete[] LT; } void Initial(void); //初始化并申请存储空间 void Input(void); //输入 void CreatLineTree(int sp,int tp,int p); //构造[sp,tp]线段树 void Solution(void); //插入区间,统计颜色 void Insert(int a,int b,int p,int color); //[a,b]:把区间[a,b]插入线段树 //p:当前所在线段树的位置 //color:当前区间的染色 void DFS(int p); //遍历线段树,计算线段树中不同颜色的个数 protected: int N; //海报数(区间数) int Maxp; //记录(压缩后的)最大端点,用于建造区间[1,Maxp]的线段树 LineTree_Node* LT; //线段树 int **reg; //顺序存储输入的区间(每张海报的宽度) int *ep; //顺序存储输入的每个区间的2个端点 unsigned short *dis; //映射端点,压缩区间(离散化) bool* tagcol; //标记能看见的颜色 int cnt; //计数器,记录线段树中能看见的不同的颜色数
}; void solve::Initial(void)
{ cnt=0; reg=new int*[N+1]; for(int i=1;i<=N;i++) reg[i]=new int[2]; ep=new int[2*N+1]; dis=new unsigned short[1e7+1]; memset(dis,0,sizeof(unsigned short)*(1e7+1)); tagcol=new bool[N+1]; memset(tagcol,false,sizeof(bool)*(N+1)); return;
} void solve::Input(void)
{ int p=0; for(int i=1;i<=N;i++) { scanf("%d %d",® [ i ][0],® [ i ][1]); /*避免相通的端点重复映射到不同的值*/ /*也为了减少参与排序的元素个数,这里必须做标记*/ /*同时为了节约空间,本用于离散化的dis[]数组暂时用来标记端点*/ if(dis[reg[i][0]]==0) { ep[p++]=reg[i][0]; dis[reg[i][0]]=1; } if(dis[reg[i][1]]==0) { ep[p++]=reg[i][1]; dis[reg[i][1]]=1; } } /*离散化*/ sort(ep,ep+p); //区间端点排序 unsigned short hash=0; for(int j=0;j<p;j++) dis[ep[j]]=++hash; //把排好序的端点依次映射到1,2,...,Maxp Maxp=hash; LT=new LineTree_Node[4*Maxp+1]; return;
} void solve::CreatLineTree(int sp,int tp,int p)
{ LT[p].s=sp; LT[p].e=tp; if(sp==tp) return; /*注意线段树不一定都是完全二叉树*/ /*但是为了处理方便,加快搜索效率*/ /*我们完全可以把线段树以完全二叉树的形式进行构造、存储*/ int mid=(sp+tp)>>1; CreatLineTree(sp,mid,p*2); CreatLineTree(mid+1,tp,p*2+1); return;
} void solve::Solution(void)
{ for(int i=1;i<=N;i++) Insert(dis[reg[i][0]],dis[reg[i][1]],1,i); //逐个区间(海报)对线段树染色 DFS(1); printf("%d\n",cnt); return;
} void solve::Insert(int a,int b,int p,int color)
{ if(b<LT[p].s || a>LT[p].e) //[a,b]与[s,e]完全无交集 return; //说明[a,b]不被[s,e]所在的子树包含,无需向下搜索 if(a<=LT[p].s && b>=LT[p].e)//[a,b]完全覆盖[s,e] { LT[p].col=color;//[s,e]染单色,暂时无需对[s,e]的子树操作(这是由线段树的搜索机制决定的) return; //因此直接返回 } /*若能执行到这里,说明[a,b]部分覆盖[s,e]*/ if(LT[p].col>=0) //[s,e]本为无色或者单色 { //由于不知道[a,b]覆盖了[s,e]多少 LT[p*2].col=LT[p*2+1].col=LT[p].col; //因此先由[s,e]的孩子继承[s,e]的单色 LT[p].col=-1; //[s,e]由于被部分覆盖,染色为多色 } /*若能执行到这里,说明[s,e]为多色*/ /*细化处理[s,e]的孩子,确定[s,e]中哪部分的区间是什么颜色*/ Insert(a,b,p*2,color); Insert(a,b,p*2+1,color); return;
} void solve::DFS(int p)
{ if(LT[p].col==0) //[s,e]为无色,其孩子也必为无色,无需继续搜索 return; if(LT[p].col>0) //[s,e]为单色,则无需向下搜索 { //因为其子区间必被[s,e]覆盖,能看见的只有[s,e]的颜色 if(!tagcol[LT[p].col]) { tagcol[LT[p].col]=true; cnt++; } return; } if(LT[p].col==-1) //[s,e]为多色,说明能在[s,e]看到集中颜色 { //搜索其子区间具体有什么颜色 DFS(p*2); DFS(p*2+1); } return;
} int main(void)
{ int test; scanf("%d",&test); for(int t=1;t<=test;t++) { int n; scanf("%d",&n); solve poj2528(n); } return 0;
}
POJ Mayor's posters——线段树+离散化相关推荐
- poj 2528 Mayor's posters (线段树+离散化)
/*离散化+线段树由于 数据的输入最大是 10000000 ,直接用开数组肯点会超,所以要将起离散话,首先 ,我们存储输入的边,将其离散化,后面的就和一般的线段树一样可. */#include< ...
- POJ-2528 Mayor's posters 线段树+离散化 或 DFS
题目大意 有 t 组数据,每组有 n 张(1<=n<=1e4)覆盖了 区间 [li,ri] 的海报(1<=i<=n,1<=li<=ri<=1e7),海报会由于 ...
- POJ - 2528 Mayor's posters(线段数+离散化)
题目链接:点击查看 题目大意:给定一个长度为1e7的墙,然后给出n张海报,每张海报都会占据墙上的一部分宽度,问按照给出的次序往墙上贴海报, 最后有几张海报能露出来(露出部分也算) 题目分析:线段树的区 ...
- poj 2528 Mayor's posters(线段树+离散化)
1 /* 2 poj 2528 Mayor's posters 3 线段树 + 离散化 4 5 离散化的理解: 6 给你一系列的正整数, 例如 1, 4 , 100, 1000000000, 如果利用 ...
- 【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)
[POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS Memory Limit: 65536K Total Submiss ...
- poj2528贴海报(线段树离散化)
//poj2528贴海报(线段树离散化) #include<cstring> #include<iostream> #include<cstdio> #includ ...
- HDOJ 2492 Ping pong 线段树+离散化
//2492 Ping pong 线段树+离散化 /* 题意: 有一陀人从左到右排成一排,每个人有一个唯一的技能值,每个人都找其他人比赛, 比赛前要再找一个人做裁判,裁判的技能值不能比这两个人都高,也 ...
- poj 2528 Mayor's posters(线段树 离散化 区间更新 贴海报)
这个题目本来对大神来说可能是水题, 对我就不行了,昨晚非折腾到下半夜一点 搞定, 并且可以总结出 ,只有把问题想清楚,或着看人家解题报告自己把问题和代码思路 搞清楚,才能谈的上调bug,否则 ...
- POJ - 2528 Mayor's posters (浮水法+线段树/离散化+线段树)
题目链接 题意: n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000) .求出最后还能看见多少张海报. 分析1 离散 ...
最新文章
- Apache Lucene 7.0即将发布!
- Spring基于 XML 的声明式事务控制(配置方式)
- maven学习(1)
- 【转】前端进阶之路:如何高质量完成产品需求开发
- 解决纵向滚屏导致的轮播图异常
- 虚拟机环境下Centos6.5如何上网
- C# Socket tcp 发送数据大小问题
- 8.Docker技术入门与实战 --- 使用Dockerfile创建镜像
- 分解原理_原理篇 | 推荐系统之矩阵分解模型
- Win10设置双网卡优先级
- git项目拉下来之后无法找到主加载类
- 把下列c语言的语句改写成汇编语言的程序片段,其中变量都为整形变量,汇编程序设计读书笔记(4)...
- IE显示对象不支持此属性或方法 的解决方法
- 深入剖析串口通信数据格式
- 本地资源库,中央资源库,远程资源库的介绍
- IntelliJ IDEA 2018.3 汉化包
- DPDK in KVM
- 银联报文中和密码相关域
- Linux中使用正则表达式进行文本匹配
- GDC翻译:Far Cry 5 的程序化世界生成(第三部分:7-生态工具(Biome Tool))