POJ 2528(线段树+离散化)

题意:n(n<=10000)个人依次贴海报, 给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。

求出最后还能看见多少张海报。(题意是我抄的...)

思路:

能够想到用线段树去进行区间更新,求最终的更新结果。但发现给的数据会得到1000万,所以普通的线段树是开不了的,但是发现n的范围为1万,可以在这上面做点文章,所以可以对这给定的n个区间进行离散化。

普通离散化的具体步骤为:对所有输入的数排序,然后进行去重,然后将数映射到1~n。

但是普通离散化在本题中是不可行的,我们注意到,每个数字其实表示的是一个单位长度(一个点也是一段),举个栗子:

1-10 1-4 6-10  直接进行算得到的ans是3

进行离散化:X[1] = 1, X[2] = 4, X[3] = 6, X[4] = 10

1-4 1-2 3-4    计算得到ans为2,结果错误!

怎么解决呢?如果相邻数字间距大于1的话, 在其中加上任意一个中间数字, 比如上面例子会加成[1, 3, 4, 5, 6, 7, 10], 然后再做线段树就ok,这样做可以防止丢失单点的成段效果。


具体代码实现如下:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <cstdio>
#include <set>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
const int maxn = 10005;
int col[maxn<<4], lazy[maxn<<4];
int inp[maxn][2], chg[maxn<<2];
set<int> hash;	//记录访问过与否 
void pushDown(int rt)
{
	if(lazy[rt])
	{
		lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
		col[rt<<1] = col[rt<<1|1] = lazy[rt];
		lazy[rt] = 0;
	}
}
void build(int l, int r, int rt)
{
	lazy[rt] = 0;
	col[rt] = 0;
	if(l == r) return;
	int m = (l+r) >> 1;
	build(lson);
	build(rson);
}
void update(int k, int L, int R, int l, int r, int rt)
{
	if(L <= l && R >= r)
	{
		col[rt] = k;
		lazy[rt] = k;
		return;
	}
	pushDown(rt);
	int m = (l+r) >> 1;
	if(L <= m) update(k, L, R, lson);
	if(R > m) update(k, L, R, rson); 
}
int query(int l, int r, int rt)
{
	if(l == r) 
	{
		if(col[rt] && !hash.count(col[rt]))
		{
			hash.insert(col[rt]);
			return 1;
		}
		return 0;
	}
	pushDown(rt);
	int m = (l+r) >> 1;
	return query(lson) + query(rson);
}
int main()
{
	int t, n, l, r, cnt;
	scanf("%d", &t);
	for(int i = 1; i <= t; ++i)
	{
		hash.clear(); cnt = 0;
		scanf("%d", &n);
		for(int j = 1; j <= n; ++j)
		{
			scanf("%d %d", &inp[j][0], &inp[j][1]);
			chg[cnt++] = inp[j][0];
			chg[cnt++] = inp[j][1];
		}
		sort(chg, chg+cnt);
		cnt = unique(chg, chg+cnt) - chg;
		int tmp = cnt;
		for(int j = 1; j < tmp; ++j)
		{
			if(chg[j] - chg[j-1] > 1) chg[cnt++] = chg[j-1]+1;
		}
		sort(chg, chg+cnt);
		build(1, cnt, 1);
		for(int j = 1; j <= n; ++j)
		{
			int a = lower_bound(chg, chg+cnt, inp[j][0]) - chg;
			int b = lower_bound(chg, chg+cnt, inp[j][1]) - chg;
			++a, ++b;
			update(j, a, b, 1, cnt, 1);
		}
		printf("%d\n", query(1, cnt, 1));
	}
	return 0;
}


在这儿再补充一下知识点。

1.unique函数

unique()函数是STL中得一个去重函数,在头文件<algorithm>中,unique的功能是去除相邻的重复元素(只保留一个),还有一个容易忽视的特性是它并不真正把重复的元素删除。
unique(num, num+n)返回的是num去重后的尾地址,之所以说比是真正把重复的元素删除,其实是,该函数把重复的元素移到后面去了,依然保存到了原数组中,然后返回去重后的最后一个元素的下一地址。因为unique去除的是相邻的重复元素,所以一般用之前都会要排一下序。

2.离散化主要代码

sort(num, num+cnt);//排序 
cnt = unique(num, num+cnt) - num;//去重 
for(int i = 1; i <= m; ++i)
{
	int k = lower_bound(num, num+cnt, input[i]) - num;
	//取对应的值在数组中的位置以对应离散值 
	++k;
	//通常使k++,另第一个离散值为1
	//同理也可通过离散值在数组中找到原数值 
}

继续加油~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值