Description
某中学有很多学生社团,其中电竞社是最受欢迎的一个。该社团中总共有N只游戏战队,但是该中学只有一个游戏训练场馆,每次只能容纳一只战队训练。
每只战队对训练时间都有一定的要求,比如甲战队想要在a到b这段时间训练,乙战队想要在c到d这段时间训练,......
作为训练场管理员的你总是收到形如(x,y)的询问,意思是查询在x到y这段时间内,最多能满足多少个只战队训练。现在有M个询问摆在你面前,请你快速做出回答!
Input
第一行,两个整数N和M。
接下来N行,每行两个整数a和b,表示一只战队训练的起止时间点。
接下来M行,每行两个整数x和y,表示一个询问的起止点。
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
*/