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;
#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;
}