POJ2528 Mayer's posters

题意:有一面长度为10^7的墙,依次往上面贴N张海报,每个海报占据一个区间[L, R],并且后贴的会盖住先贴的。现在依次给出N张海报占据的区间,求最后能看到几张海报。(完全被盖住就看不到了)

N <=10000

 

         这题虽然出现在树堆练习题当中,然而我并没有想出如何用树堆……似乎只是用线段树就可以了。

         思路就是,对于海报1~N,如果海报i占据一个区间[L, R],就对这个区间赋值为i(用赋值标记),后来的标记覆盖之前的标记。最后统计的时候,开一个bool数组visible统计各个海报是否出现。对整棵线段树DFS一遍,如果遇到赋值标记(称为setv)不为0,意味着这个子区间已经全被赋值为setv,那么就把setv的出现记录到visible当中。最后遍历visible即可。

         还有就是这题需要离散化(对长为10^7的区间开线段树太大了,然而海报只有10000张,所以坐标值只有20000个)。于是把各个海报的坐标离散化到20000以内,然后对区间[1, 20000]开线段树就可以了。注意重复的坐标值在离散化之后仍然需要是一样的值,因此求出rank -> pos的映射之后,求pos -> rank的映射不是简单的反函数,需要判断与前一个rank是否相等。(就是后缀数组里面rank的求法……)


#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#define LC 2*o
#define RC 2*o+1

using namespace std;

int N;

int setv[80007]={};

void pushdown(int o)
{
	if(setv[o] > 0)		setv[LC] = setv[RC] = setv[o];
	setv[o] = 0;
}

// op interval = [y1, y2]
void Set(int o, int l, int r, int y1, int y2, int val)
{
	if(y1 <= l && r <= y2)
	{
		setv[o] = val;
		return;
	}
	pushdown(o);
	int m = (l+r)/2;
	if(y1 <= m)	Set(LC, l, m, y1, y2, val);
	if(y2 >= m+1)	Set(RC, m+1, r, y1, y2, val);
}

bool visible[10007]={};
void stat(int o, int l, int r)
{
	if(setv[o] > 0)
	{
		visible[setv[o]] = true;
		return;
	}
	if(l == r)	return;
	int m = (l+r)/2;
	stat(LC, l, m);
	stat(RC, m+1, r);
}

int S[20007]={};	//start and end	
int sa[20007]={};
int rank[20007]={};	//start and end	(discrete)

bool cmp(int i, int j)
{
	return S[i] < S[j];
}

int main()
{
	int C;
	scanf("%d", &C);
	while(C--)
	{
		memset(setv, 0, sizeof(setv));
		memset(visible, 0, sizeof(visible));
		
		scanf("%d", &N);
		for(int i = 1; i <= N; i++)
		{
			int s,e;
			scanf("%d%d", &S[2*i-1], &S[2*i]);
		}
		// discretion
		for(int i = 1; i <= 2*N; i++)	sa[i] = i;
		sort(sa+1, sa+2*N+1, cmp);
		int p = 0;
		for(int i = 1; i <= 2*N; i++)
		{
			if(S[sa[i]] == S[sa[i-1]])	rank[sa[i]] = p;
			else		rank[sa[i]] = ++p;
		}
		
		for(int i = 1; i <= N; i++)		Set(1, 1, 20000, rank[2*i-1], rank[2*i], i);
		
		stat(1, 1, 20000);
		int cnt = 0;
		for(int i = 1; i <= N; i++)
			if(visible[i])	cnt++;
		printf("%d\n", cnt);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值