训练场(倍增练习)

Description

  某中学有很多学生社团,其中电竞社是最受欢迎的一个。该社团中总共有N只游戏战队,但是该中学只有一个游戏训练场馆,每次只能容纳一只战队训练。 
  每只战队对训练时间都有一定的要求,比如甲战队想要在ab这段时间训练,乙战队想要在cd这段时间训练,...... 
  作为训练场管理员的你总是收到形如(x,y)的询问,意思是查询在xy这段时间内,最多能满足多少个只战队训练。现在有M个询问摆在你面前,请你快速做出回答!

Input

  第一行,两个整数NM 
  接下来N行,每行两个整数ab,表示一只战队训练的起止时间点。 
  接下来M行,每行两个整数xy,表示一个询问的起止点。

Output

  M行,每行一个整数,表示一次询问的答案。

Sample Input

3 2

1 2

2 3

1 3

1 2

1 3

Sample Output

1

2

Hint

【数据范围】 
  x < y同时a < b 0 <= x,y,a,b <= 1,000,000,000 
  对于30%的数据,0 < N,M <= 2000 
  对于50%的数据,0 < N,M <= 50000 
  对于100%的数据,有0 < N,M <= 100000

今天下午讲了倍增,真是一个优秀的思想。

首先,这道题满足贪心策略(活动选择)。

感性证明:排序之后,L升序且r升序,只关心以后的最优策略,假设选择一个点j后,存在一个最优策略,如果我们选择i(i<j)不会让最优解更差,又因为选j是最优策略,所以选i也是最优策略。所以我们可以采用贪心策略。

定义f[i][j]为第i点向右的第2^j个合法状态:

(1,3),(2,4),(3,5),(4,6),(7,9)

f[1][0]=3,f[1][1]=5……

然后我们就可以拼出这个区间~

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100005;
struct Team{
	int st,ed;
	bool operator <(const Team&A) const {
		return st<A.st||st==A.st&&ed>A.ed;
	}
}t[Maxn],s[Maxn];
int n,m,cnt;
int f[Maxn][17];//第i个线段往后2^j个合法线段的编号 
void ST(){
	for(int i=1,j=1;i<=cnt;++i){
		while(j<=cnt&&s[j].st<s[i].ed)++j;
		f[i][0]=j;
	}
	int maxlog=log2(cnt);
	for(int j=1;j<=maxlog;++j)
		for(int i=1;i<=cnt;++i)
			f[i][j]=f[f[i][j-1]][j-1];
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		int l,r;scanf("%d%d",&l,&r);
		t[i]=(Team){l,r};
	}
	sort(t+1,t+n+1);
	for(int i=1;i<=n;++i){
		while(cnt&&s[cnt].ed>t[i].ed)--cnt;
		s[++cnt]=t[i];
	}
	s[0]=(Team){1<<30,1<<30};
	s[cnt+1]=(Team){1<<30,1<<30};
	ST();
	for(int i=1;i<=m;++i){
		int x,y;scanf("%d%d",&x,&y);
		int p=lower_bound(s+1,s+cnt+1,(Team){x,1<<30})-s;
		int ret=s[p].ed<=y,maxlog=log2(cnt);//记得算上自己 
		for(int j=maxlog;j>=0;--j)if(s[f[p][j]].ed<=y){
			if(!f[p][j]||f[p][j]==n+1)break;
			ret+=1<<j;p=f[p][j];
		}
		printf("%d\n",ret);
	}
	return 0;
}
/*
4 2
1 2
2 6
3 8
7 10
2 10
5 10
*/

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值