POJ 2528 Mayor's posters

n(n<=10000) 个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000) 。求出最后还能看见多少张海报。
Input
       第一行: 样例个数T
       第二行: 贴海报的人n
       第三行: 每个人贴海报的范围
        接下来n行: 每个人贴海报的范围
Output
对于每一个输入,输出最后可以看到的海报张数。下面这个图是样例解释
 
 

Sample Input
1
5
1 4
2 6
8 10
3 4
7 10
Sample Output
4

 刚学的线段树,一直以为线段树只是求区间的问题,套套模板就行了,可是这道题套不成,花了两天时间才写出来。首先线段树大多数解决的是1e4~1e6数据范围,可是这道题范围太大,暴力不是超时就是超内存,所以只能离散化处理。   怎么离散化才能够更加合理呢?因为本题的区间范围太大,不能用到线段树,所以我们可以在缩小区间上面做文章。比如:1 4, 5 10,这两个区间,我们可以让这四个数,一一对应一个数来代表它们,不用想肯定是下标最合适,所以1就对应1,2对应4,3对应5,4对应10,区间对应之后就是1 2,3 4; 这样离散化还没有结束,因为还有这种情况比如:1 10,1 4,6 10,按照刚刚那种对应关系,区间对应之后就,1 4,1 2,3 4,对应之后只能看到两种颜色,可是这种情况能看到三种颜色,因为4~6之间还有一种颜色,可是由于离散化给忽视了,解决的方法是我们可以在4~6之间再加一个区间5,这样的话5就代表了一种颜色,而不被忽视。具体的离散化请看下列代码分析:

		//先将所有的端点存入坐标中进行排序 
		int t = 0;
		scanf("%d", &N);
		for(int i = 1; i <= N; i++) {
			scanf("%d %d", &s[i].left, &s[i].right);
			num[++t] = s[i].left;//下标从1开始 
			num[++t] = s[i].right;
		}
		sort(num+1, num+t+1);//从小到大排序 
 接下来处理被离散化而忽视的那种情况。排过序之后,如果每个端点相差大于1,那么就在这两个端点之间再加一个端点,可是如果两个端点相等就没有意义,因为离散化就是一一对应,没必要对重复的数进行一一对应,所以还需要一步去重,去过重之后,然后再进行上一步的离散化处理。

		//去重处理 
		int m = unique(num+1, num+t+1)-num;
		/*这里需要多注意,因为用到了下标,下标一一对应,所以下标不管是从
0开始,还是从1开始都要进行一一对应,不然就会导致离散化出错 
		*/ 
		int p = m-1; 
		for(int i = 1; i < p; i++) {
			if(num[i+1]-num[i] > 1)//因为已经按照升序排序,以及去重处理,所以num[i+1]必大于num[i] 
			num[m++] = num[i]+1;//在两者之间加上一个数 
		}
		//处理完之后,再进行一步排序 
		sort(num+1, num+m);
这样离散化就好了,然后就是线段树的操作了,为了更快找到对应的端点,直接用二分查找就可以了。具体请看代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
struct node{
	int left;
	int right;
}s[maxn]; 
bool hash[maxn];
int num[maxn<<4];
int value[maxn<<4];

void Pushdown(int i) {//延迟标记 
	value[i<<1] = value[i<<1|1] = value[i];
	value[i] = -1;
}

void Update(int numl, int numr, int C, int l, int r, int i) {
	if(numl <= l && numr >= r) {//找到区间 
		value[i] = C;
		return ;
	}
	if(value[i] != -1) Pushdown(i);//更新下个区间 
	int m = (l+r)>>1;
	if(numr <= m) Update(numl, numr, C, l, m, i<<1);
	else if(numl > m) Update(numl, numr, C, m+1, r, i<<1|1);
	else {
		Update(numl, m, C, l, m, i<<1);
		Update(m+1, numr, C, m+1, r, i<<1|1);
	}
	return ;
}
int ans;
void Query(int i, int l, int r) {
	if(value[i] != -1) {
		if(hash[value[i]] == false) {
			ans++;
			hash[value[i]] = true;
		}
		return;
	}
	if(l == r) return ;
	int m = (l+r)>>1;
	Query(i<<1, l, m);
	Query(i<<1|1, m+1, r);
}
int main() {
	int T;
	int N;
	scanf("%d", &T);
	while(T--) {
		memset(value, -1, sizeof(value));
		memset(hash, false, sizeof(hash));
		int t = 0;
		scanf("%d", &N);
		for(int i = 1; i <= N; i++) {
			scanf("%d %d", &s[i].left, &s[i].right);
			num[++t] = s[i].left;//下标从1开始 
			num[++t] = s[i].right;
		}
		//处理一 
		sort(num+1, num+t+1);
		//去重处理 
		int m = unique(num+1, num+t+1)-num;
		int p = m-1; 
		//处理二 
		for(int i = 1; i < p; i++) {
			if(num[i+1]-num[i] > 1)
			num[m++] = num[i]+1;
		}
		sort(num+1, num+m);
		for(int i = 1; i <= N; i++) {
			int l = lower_bound(num+1, num+m, s[i].left)-num;//二分查找 
			int r = lower_bound(num+1, num+m, s[i].right)-num;
			Update(l, r, i, 1, m-1, 1);//更新 
		}
		ans = 0;
		Query(1, 1, m-1);//查询 
		printf("%d\n", ans);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值