【POJ 2528】线段树求解矩形覆盖

1.题目链接。给定一些海报,可能相互重叠,告诉你每个海报的宽度(高度都一样的)和先后叠放顺序,问没有被完全盖住的有多少张?海报最多10000张,但是墙有10000000块瓷砖长,海报不会落在瓷砖中间。
如果直接建树,就算不TLE,也会MLE。即单位区间长度太多。其实10000张海报,有20000个点,最多有19999个区间。对各个区间编号,就是离散化。然后建数。其实浮点数也是一样离散化的。

2.这个题目的离散化其实是很好理解的,但是怎么和线段树联系起来这个似乎有点问题。简单分析一下,假设我们离散化之后,得到了n个点,每张海报其实占据的就是两个点,然后我们就是考察一下这些点组成的线段之间的覆盖关系。对于每张海报的两个点,其实我们把他贴上去,就是在更改这个海报对应的两个点的状态,这些状态指的是:这些点有没有被访问过?被访问了几次?等等。其实我们这里关心的就是这两个点有没有被访问过。这里就涉及到点修改,其实也可以说成区间的修改,我们可以把对点修改看成对差分数组的点修改,对应原数组就是区间修改。然后我们开始贴海报,从后往前贴,为什么,很简单,因为我们最后贴上去的海报是在表面的,所以前边贴上来的有可能被覆盖。其实说多了不是很好,拿起笔自己模拟一下整个程序的流程,就可以清晰的明白代码细节。

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 10010;
#pragma warning(disable:4996)
struct Cpost
{
	int l, r;
}posters[MAXN];
int x[MAXN * 2];
int Hash[10000005];
struct Node
{
	int l, r;
	bool bcovered;
}seg[MAXN*8];
void build(int n, int l, int r)
{
	seg[n].l = l;
	seg[n].r = r;
	seg[n].bcovered = false;
	if (l == r)return;
	int mid = (l + r) >> 1;
	build(n << 1, l, mid);
	build(n << 1 | 1, mid + 1, r);
}
bool post(int n, int l, int r)
{
	if (seg[n].bcovered)return false;
	if (seg[n].l ==l && seg[n].r == r)
	{
		seg[n].bcovered = true;
		return true;
	}
	bool bResult;
	int mid = (seg[n].l + seg[n].r) >> 1;
	if (r <= mid)bResult = post(n << 1, l, r);
	else if (l > mid)bResult = post(n << 1 | 1, l, r);
	else
	{
		bool b1 = post(n << 1, l, mid);
		bool b2 = post(n << 1 | 1, mid + 1, r);
		bResult = b1 || b2;
	}
	if (seg[n << 1].bcovered&&seg[n << 1 | 1].bcovered)
		seg[n].bcovered = true;
	return bResult;
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		int cnt = 0;
		for (int i = 0; i < n; i++)
		{
			scanf("%d%d", &posters[i].l, &posters[i].r);
			x[cnt++] = posters[i].l;
			x[cnt++] = posters[i].r;
		}
		sort(x, x + cnt);
		int ncnt = unique(x, x + cnt) - x;
		for (int i = 0; i < ncnt; i++)
			Hash[x[i]] = i;
		build(1, 0, ncnt - 1);
		int res = 0;
		for (int i = n - 1; i >= 0; i--)
		{
			if (post(1, Hash[posters[i].l], Hash[posters[i].r]))
				res++;
		}
		printf("%d\n", res);
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值