首页 > 代码库 > TreeSegment2528

TreeSegment2528

有一面墙,被等分为1QW份,一份的宽度为一个单位宽度。现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW。后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报。现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到。)

 

/*

 * my_poj2528.cpp

 *

 *  Created on: 2012-6-15

 *      Author: ict

 */

 

#include <cstring>

#include <cstdlib>

#include <cstdio>

#include <algorithm>

using namespace std;

 

#define MAX 20010

 

#define LC(x) ((x) << 1) //计算左孩子位置

#define RC(x) (((x) << 1) + 1) //计算右孩子位置

 

struct Node

{

int l; //线段左端点值

int r; //线段右端点

int c; //线段的颜色

}nodes[MAX * 5];

 

int map[MAX][2]; //记录输入线段的左右两个端点

int record[MAX]; //记录颜色是否已经出现

int total;

 

//用于离散化

struct Line

{

int point; //记录端点的坐标

int num; //记录原来的编号

}line[MAX * 2];

 

int mycmp(const Line &a, const Line &b)

{

return a.point < b.point;

}

 

void buildTree(int l, int r, int position)//buildTree(1, count, 1);

{

nodes[position].l = l;

nodes[position].r = r;

nodes[position].c = 0; //初始化颜色都为0

if(l == r)

return ;

 

int mid = (l + r) >> 1;

buildTree(l, mid, LC(position)); //构造左子树

buildTree(mid + 1, r, RC(position)); //构造右子树

 

return ;

}

/*

 * @para l, r: 线段的左、右端点

 * @para position: 插入树的位置

 * @para color: 线段的颜色

 */

 

void insert(int l, int r, int position, int color)//insert(map[i][0], map[i][1], 1, i + 1);i个海报对应i+1号色

{

if(nodes[position].l == l && nodes[position].r == r) //刚好覆盖,修改颜色,直接返回

{

nodes[position].c = color;

return ;

}

 

if(nodes[position].c > 0) //pushdown

{

nodes[LC(position)].c = nodes[position].c;

nodes[RC(position)].c = nodes[position].c;

nodes[position].c = 0; // 这就是pushup,父节点颜色是混乱的,用0表示

}

 

if(l >= nodes[RC(position)].l) //完全在右子树

insert(l, r, RC(position), color);

else

if(r <= nodes[LC(position)].r) //完全在左子树

insert(l, r, LC(position), color);

else //两个子树都有

{

insert(l, nodes[LC(position)].r, LC(position), color);

insert(nodes[RC(position)].l, r, RC(position), color);

}

 

}

 

/*

 * @para position: 查询指定线段的颜色

 */

void update(int position)

{

if(nodes[position].c != 0) //整个根节点以下是同一个颜色,统计该色

{

if(!record[nodes[position].c])//是否存在过

{

total++;

record[nodes[position].c] = 1;

}

return ;

}

//不是同一个颜色,是混合的

update(LC(position));

update(RC(position));

return ;

}

 

int main()

{

int number;

int n;

int i;

scanf("%d", &number);

while(number--)

{

scanf("%d", &n);

 

for(i = 0; i < n; i++)

{

scanf("%d%d", &map[i][0], &map[i][1]);

line[2*i].point = map[i][0]; //记录数据,用于离散化

line[2*i + 1].point = map[i][1];

line[2*i].num = -(i + 1); //线段第一个端点用负数记录

line[2*i + 1].num = i + 1; //线段第二个端点用正数记录

}

 

//首先将所有端点排序

sort(line, line + 2*n, mycmp);

int temp = line[0].point;

int count = 1; //重新开始编号,从1开始

for(i = 0; i < 2*n; i++)

{

if(temp != line[i].point) //如果当前端点和前面的端点不一样,编号值+1

{

count++;

temp = line[i].point;

}

 

/*离散就要考虑去重。这里离散的过程:

map  : [-3,2] [-1,4]  [-2,-1] 表示map[0][0]=-3,map[0][1]=2

line.point: [-3,2] [-1,4]  [-2,-1] 表示line.point[0]=-3 line.point[1]=2

line.num: [-1,1] [-2,2]  [-3,3]  表示line.num[0]=-1,line.num[1]=1

sort,line.point:  [-3,-2][-1,-1] [2,4]

sort后,line.num:   [-1,-3][-2,3]  [1,2]   

得到新的map:        [1,4]  [3,5]    [2,3]

对应旧的map         [-3,2] [-1,4]  [-2,-1],大小相对关系不变,次序也不变

其实跟一维离散一样的道理,point记录原始值大小,num记录本来的次序

根据point排序后,用map[-line[i].num - 1][0] = count离散

其中,-line[i].num - 1表示以前处于什么次序,现在还在那个次序

count去除重复后,从1递增,因为排序后line[i].point也是递增,即以前大小

排第几,现在还是排第几

  */

 

if(line[i].num < 0)

map[-line[i].num - 1][0] = count;

else

map[line[i].num - 1][1] = count;

}

 

buildTree(1, count, 1);

 

for(i = 0; i < n; i++)//按次序插入

insert(map[i][0], map[i][1], 1, i + 1);

 

memset(record, 0, sizeof(record));

total = 0;

update(1);

printf("%d\n", total);

}

 

return 0;

}

 

TreeSegment2528