hihoCoder1079 离散化 优先队列+贪心

描述

小Hi和小Ho在回国之后,重新过起了朝7晚5的学生生活,当然了,他们还是在一直学习着各种算法~

这天小Hi和小Ho所在的学校举办社团文化节,各大社团都在宣传栏上贴起了海报,但是贴来贴去,有些海报就会被其他社团的海报所遮挡住。看到这个场景,小Hi便产生了这样的一个疑问——最后到底能有几张海报还能被看见呢?

于是小Ho肩负起了解决这个问题的责任:因为宣传栏和海报的高度都是一样的,所以宣传栏可以被视作长度为L的一段区间,且有N张海报按照顺序依次贴在了宣传栏上,其中第i张海报贴住的范围可以用一段区间[a_i, b_i]表示,其中a_i, b_i均为属于[0, L]的整数,而一张海报能被看到当且仅当存在长度大于0的一部分没有被后来贴的海报所遮挡住。那么问题就来了:究竟有几张海报能被看到呢?

思路 这题其实是用来练习离散化和线段树的,不过正好有个贪心的思路就试了试...排序的策略得想的很细,也费了不少劲..

总的想法就是,类似括号匹配的想法,嵌套在内部的括号是不能被计算如ans的。把所有的左端点和右端点一起排序,并且保存端点的编号,和是左边界还是右边界。具体排序策略:端点从小到大,若端点相等,则是左边界的在前,若边界也相同,则若都是左边界,编号大的在前,否则编号小的在前。这步很重要,影响着后面的贪心...

维护一个最大值堆,也就是一个优先队列。

然后从左到右的遍历这些端点,遇到之前没有遇到过编号的端点则入队,否则队头开始迭代的向后看时候对于当前编号的节点是否左右端点匹配,若匹配了则出队,否则结束迭代。每次看队头的元素的编号是否之前没有累加过,是则ans++,并且标记该编号节点被累加过。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <algorithm> 
#include <assert.h>
using namespace std;
typedef pair<int,pair<int,int> > pii;
#define fi first
#define se second.fi
#define th second.second
#define mp make_pair

const int maxn = 100005;
int hash1[maxn];
int mark[maxn];
int del[maxn];
pii a[maxn<<1];
priority_queue<int> pq;
int n,l;

bool cmp(pii x,pii y){
	if(x.fi != y.fi)
		return x.fi < y.fi;
	if(x.th != y.th)
		return x.th < y.th;
	if(x.th == 1)
		return x.se > y.se;
	else
		return x.se < y.se;
}

int main(){
	scanf("%d%d",&n,&l);
	memset(hash1,0,sizeof(hash1));
	memset(mark,0,sizeof(mark));
	
	for(int i=0;i<n;i++){
		scanf("%d%d",&a[i<<1].fi,&a[(i<<1)|1].fi);
		a[i<<1].se = i;
		a[i<<1].th = 1;
		a[(i<<1)|1].se = i;
		a[(i<<1)|1].th = 2;
	}
	sort(a,a+(n<<1),cmp);
	int ans = 0;
	for(int i=0;i<(n<<1);i++){
		if(hash1[a[i].se]==0){
			hash1[a[i].se] = 1;
			pq.push(a[i].se);
			if(mark[pq.top()] == 0)
			{
				ans++;
				mark[pq.top()] = 1;
			}	
		}
		else{
			hash1[a[i].se] = 2;
			if(hash1[pq.top()]==2){
				while(pq.size()>0 && hash1[pq.top()]==2){
					pq.pop();
				}
				if(pq.size()>0 && mark[pq.top()] == 0)
				{
					ans++;
					mark[pq.top()] = 1;
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值