POJ 2528 Mayor's posters(线段树)

题目大意:在墙壁上贴广告,广告的版面有大有小,并且贴广告有先后之分,后面贴的广告会覆盖前面的广告,求解最后能看到的广告面

题目分析
更新的基本原理就是,当我们贴海报i时,如果遇到某一线段区间能够被当前海报完全覆盖,那么我们更新到此区间即可,即让它的颜色更新,记录下新c值。如果不能被海报完全覆盖,则只是这一线段区间的部分区间需要修改,则更改这一区间的子区间的颜色即可。

#include<iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
struct Node
{
	int l;	//线段左端点值
	int r;	//线段右端点
	int c;	//线段的颜色
}nodes[100000];

int map[30000][2];	//记录输入线段的左右两个端点
int record[30000];	//记录颜色是否已经出现
int total;
struct Line
{
	int point;	//记录端点的坐标
	int num;	//记录原来的编号
}line[100000];

int cmp(const Line &a, const Line &b)
{
	return a.point < b.point;
}

void build(int l, int r, int p)
{
	nodes[p].l = l;
	nodes[p].r = r;
	nodes[p].c = 0;	//初始化颜色都为0
	if(l != r)
    {
        int mid = (l + r) >> 1;
	    build(l, mid, p<<1);	//构造左子树
	    build(mid + 1, r, p<<1|1);	//构造右子树
    }
}
void insert(int l, int r, int p, int c)
{
	if(nodes[p].l != l || nodes[p].r != r)
    {
        if(nodes[p].c > 0)	//如果当前线段已经有颜色,先将颜色复制给左右两个子树,非常重要
	    {
		    nodes[p<<1].c = nodes[p].c;
		    nodes[p<<1|1].c = nodes[p].c;
		    nodes[p].c = 0;	//标记线段没有颜色
	    }
	    if(l >= nodes[p<<1|1].l)	//完全在右子树
		    insert(l, r, p<<1|1, c);
        else if(r <= nodes[p<<1].r)	//完全在左子树
			insert(l, r, p<<1, c);
        else		//两个子树都有
        {
           insert(l, nodes[p<<1].r, p<<1, c);
           insert(nodes[p<<1|1].l, r, p<<1|1, c);
        }
    }
    else//刚好覆盖,修改颜色,直接返回
    {
		nodes[p].c = c;
    }
}
void update(int p)
{
	if(nodes[p].c == 0)
	{
        //如果当前线段没有颜色,递归调用左右子树,查询颜色
	     update(p<<1);
	     update(p<<1|1);
	}
	//如果当前线段有颜色,记录,并且直接返回
	else
    {
        if(!record[nodes[p].c])
		{
			total++;
			record[nodes[p].c] = 1;
		}
    }

}
int main()
{
	int m;
	int n;
	int i;
	scanf("%d", &m);
	while(m--)
	{
		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, cmp);
		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;
			}

			if(line[i].num < 0)
				map[-line[i].num - 1][0] = count;
			else
				map[line[i].num - 1][1] = count;
		}

		build(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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值