训练场(倍增练习)

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;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+10;
struct P{
	int L,r;
}a[N];
int n,m,cnt;
bool cmp(const P&A,const P&b){
	return A.L==b.L?A.r>b.r:A.L<b.L;
}
inline void init(){
	P b[N];
	scanf("%d%d",&n,&m);
	Inc(i,1,n){
		int x,y;scanf("%d%d",&x,&y);
		b[i]=(P){x,y};
	}
	sort(b+1,b+1+n,cmp);
	Inc(i,1,n){
		while(cnt&&a[cnt].r>b[i].r)--cnt;
		a[++cnt]=b[i];
	}
	a[0]=a[cnt+1]=(P){1<<30,1<<30};
}
int f[N][17];//第i个线段开始往后的第2^j个合法线段编号 
inline void ST(){
	for(int i=1,j=1;i<=cnt;++i){
		while(j<=cnt&&a[j].L<a[i].r)++j;
		f[i][0]=j;
	}
	int Maxlog=log2(cnt);
	Inc(j,1,Maxlog)Inc(i,1,cnt)f[i][j]=f[f[i][j-1]][j-1];
}
struct cmpL{
	bool operator() (const P&A,const P&b)const{
        return A.L<b.L;
    }
};
inline void solv(){
	while(m--){
		int x,y;scanf("%d%d",&x,&y);
		int ans=0,j,p=lower_bound(a+1,a+cnt+1,(P){x,y},cmpL())-a;
		while(233){
			for(j=log2(cnt);j>=0&&(a[f[p][j]].r>y);--j);
			ans+=1+(j>=0?1<<j:0);
			if(~j)--ans,p=f[p][j];
			else {if(a[p].r>y)--ans;break;}
		}
		cout<<ans<<"\n";
	}
}
int main(){
	init();
	ST();
	solv();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DancingZ/article/details/81415143
今日推荐