POJ2528_Mayor's posters(线段树区间染色)

题意:

一个无限长的板子,然后依次往上面贴n张和板子等高的海报,问你最后能看到多少张海报(漏出来就算)

思路:

https://www.cnblogs.com/xuejianye/p/5694750.html
线段树区间更新问题,但是要注意,给的长度的可能非常大,有1e9,不加处理直接维护一个线段树肯定会MLE,TLE,但是我们注意到一共最多只有2e4个点,因此我们可以用离散化的思想先对区间进行预处理,所谓的离散化,

将一个很大的区间映射为一个很小的区间,而不改变原有的大小覆盖关系,但是注意简单的离散化可能

会出现错误,给出下面两个简单的例子应该能体现普通离散化的缺陷:
例子一:1-10 ,1-4, 5-10
例子二:1-10 ,1-4, 6-10
普通离散化后都变成了[1,4],[1,2],[3,4]
线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
例子一是完全被覆盖掉了,而例子二没有被覆盖

解决的办法则是对于距离大于1的两相邻点,中间再插入一个点。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson root<<1, l, mid
#define rson root<<1|1, mid+1, r
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 10000+50;
int ans, Tree[maxn<<3], L[maxn], R[maxn], tmp[maxn<<2];
bool vis[maxn<<2];

void push_down(int root){
	if(Tree[root] == 0) return;
	Tree[root<<1] = Tree[root<<1|1] = Tree[root];
	Tree[root] = 0;
}
void update(int la, int rb, int l, int r, int root, int val){
	if(la > r||l > rb) return;
	if(la <= l&&rb >= r){
		Tree[root] = val;
		return;
	}
	push_down(root);
	int mid = (l+r) >> 1;
	if(la <= mid)
		update(la, rb, l, mid, root<<1, val);
	if(rb > mid)
		update(la, rb, mid+1, r, root<<1|1, val);
}
void Query(int root, int l, int r){
	if(Tree[root]){
		if(!vis[Tree[root]]){
			++ans;
			vis[Tree[root]] = true;
		}
		return;
	}
	if(l == r) return;
	push_down(root);
	int mid = (l+r) >> 1;
	Query(lson);
	Query(rson);
}

int main()
{
	//freopen("in.txt","r",stdin);
	int T, n; scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		memset(Tree, 0, sizeof(Tree));
		int len = 0;
		for(int i = 1; i <= n; ++i){
			scanf("%d%d",&L[i],&R[i]);
			tmp[len++] = L[i];
			tmp[len++] = R[i];
		}
		// 离散化 
		sort(tmp, tmp+len);
		len = unique(tmp, tmp+len) - tmp;
		int m = len;
		for(int i = 0; i < m-1; ++i){
			if(tmp[i+1] - tmp[i] > 1) tmp[len++] = tmp[i] + 1;
		}
		sort(tmp, tmp+len);
		for(int i = 1; i <= n; ++i){
			int a = lower_bound(tmp, tmp+len, L[i]) - tmp+1;
			int b = lower_bound(tmp, tmp+len, R[i]) - tmp+1;
			update(a, b, 1, len, 1, i);
		}
		ans = 0;
		memset(vis, 0, sizeof(vis));
		Query(1, 1, len);
		printf("%d\n", ans);
	}
	fclose(stdin);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值