【线段树+离散化】Cover

Cover

cover.cpp/pas/c/1s/256M

【题目描述】

   有 N 个时间段,某个时间段可能包含其它时间段。

   请找出能包含其它时间段最多的那个段,并计算出它包括的其它时间段有多少? 

【数据范围】

 1 <= N <= 25,000

 1 <= 时间段开始和结束点 <= 2,000,000,000

【输入文件】

 文件名:cover.in

 第1行:一个整数 N

 第2..N+1行:第i+1行有两个整数A,B(1 <= A < B <= 2,000,000,000)表示第 i个时间段的开始和结束端点。任意两个时间段的端点不相同。

【输出文件】

  文件名:cover.out

 一行,一个整数。表示一段最多可包含的时间段数的最大值。

样例

输入
4
  1 7
  2 3
  5 6
  4 10   
 *-----------*
     |       |
  *-----------*
  |      |
  | *-*  *-* |
  | | |  | | |
  1 2 3 4 5 6 7 8 9 10
输出 
2
 第1段包含2,3两段   





先说说O(N2)常规方法,很好写,一个二重循环,然后判断 l 和 r 即可


但是看看范围,显然不能在规定时间内出解


这一题可以用单调队列来做,不过这里说另一种方法


如果做过 星星POJ2352 那道题的话,这应该会有点似曾相识的感觉

我们可以按照 r 进行排序,保证前面线段的R肯定是在当前线段R的左边,那么我们就只需要看前面那个线段的L是不是在当前线段的 [ L , R ] 这个区间内就可以知道当前线段是否包含前面那个线段!

这样就可以按照那道题的方法(那道题的题目是排好了序的),扫描一次,每次查找线段树(附加信息是维护的线段的左端点)内有几个是在当前区间内,再插入当前线段左端点即可

但是我们看看数据范围,N≤25000,但是端点的坐标是在 [ 1 , 2000000000 ] 这个区间内的,显然线段树是开不下的!但是我们发现N远远小于这个范围,并且题目说了任意两个端点不同,所以我们可以离散化!将 [ 1 , 2000000000 ] 这个区间内的数排个序,然后依次标号1...N*2,这样我们就可轻松的 A 了

不过离散化我不是怎么会写,所以就暴力了一下,用了一下map,所以有几组时间会有一点点久~~


C++ Code

/*
C++ Code
http://blog.csdn.net/jiangzh7
By Jiangzh
*/
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;

const int MAXN=25000+10;

int n;
struct node{int l,r;}a[MAXN];
int newa[MAXN*2],cnt=0;
map<int,int> list;
int sum[MAXN*2*4];

void read()
{
	freopen("cover.in","r",stdin);
	freopen("cover.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		newa[cnt++]=a[i].l; newa[cnt++]=a[i].r;
	}
}

bool cmp(node a,node b)
{
	if(a.r==b.r) return a.l>b.l;
	return a.r<b.r;
}

void add_tree(int p,int l,int r,int a)
{
	if(l==r && l==a){sum[p]++;return;}
	int m=(l+r)>>1;
	if(a<=m) add_tree(p<<1,l,m,a);
	if(a>m) add_tree((p<<1)+1,m+1,r,a);
	sum[p]++;
}

int count(int p,int l,int r,int a,int b)
{
	if(a<=l&&b>=r) return sum[p];
	int m=(l+r)>>1,x1=0,x2=0;
	if(a<=m) x1=count(p<<1,l,m,a,b);
	if(b>m) x2=count((p<<1)+1,m+1,r,a,b);
	return x1+x2;
}

void predoing()
{
	sort(newa,newa+cnt);
	for(int i=0;i<cnt;i++) list[newa[i]]=i;
	for(int i=1;i<=n;i++)
	{
		a[i].l=list[a[i].l];
		a[i].r=list[a[i].r];
	}
	//for(int i=1;i<=n;i++) printf("%d %d\n",a[i].l,a[i].r);
}

void work()
{
	predoing();//离散化
	sort(a+1,a+1+n,cmp);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int cnt=count(1,0,n*2,a[i].l,a[i].r);
		ans=max(ans,cnt);
		add_tree(1,0,n*2,a[i].l);
	}
	printf("%d",ans);
}

int main()
{
	read();
	work();
	return 0;
}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值