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
【分析】
离散化+倍增
通过离散化,我们把坐标范围由0<=x,y,a,b<=1,000,000,000
修改成了0<=x,y,a,b<=400,000
f[i][j]表示从坐标点i往右经过不相交的2^j个区间后,第2^j区间的右端点坐标的最小值。
那么我们很容易得出倍增法则:f[i][j]=f[f[i][j-1]][j-1];
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int INF=999999999;
struct wjj{int L,R;}Seg[MAXN<<1]; //0~N-1存区间,N~N+M-1存询问
struct node{int Id,v;}Pot[MAXN<<2]; //存坐标,方便离散化
int N,M,cnt=-1,s,tot=-1,Num=0,z;
int f[MAXN<<2][20]; //倍增数组
void _in(int &x)
{
char t=getchar();
while(t<'0'||'9'<t) t=getchar();
for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _out(int x,int j=1)
{
char t[100];
if(x==0)printf("0");
for(;x;x/=10,j++) t[j]=x%10+'0';
for(j--;j;j--)putchar(t[j]);
putchar('\n');
}
void _init()
{
int x,y;
_in(N);_in(M);
z=N+M;
for(int i=1;i<=z;i++)
{
_in(x);_in(y);
Pot[++tot].v=x;Pot[tot].Id=tot; //将所有坐标放入Pot
Pot[++tot].v=y;Pot[tot].Id=tot;
//Id为奇数,则Pot为左端点
//否则为右端点
}
}
void _del()
/*
若存在区间[x1,y1],[x2,y2]
若x1<=x2&&y2<=y1
则我们可以删除[x1,y1]这个区间影响答案
而且我写的比较丑,貌似不删除的话倍增要错
*/
{
bool flag;
for(int i=0;i<N;i++)
{
flag=true;
for(int j=i+1;j<N;j++)
{
if(Seg[i].L<=Seg[j].L&&Seg[j].R<=Seg[i].R)
{
flag=false;
break;
}
if(Seg[j].L>=Seg[i].R)
break;
}
if(flag)
Seg[++cnt]=Seg[i];
} //cnt为删除后的区间数量
}
int _query(int x,int y) //查询答案
{
int ans=0;
for(int i=s;i>=0;i--) //s为全局变量,是倍增最大深度
if(f[x][i]<=y)
{
ans+=(1<<i);
x=f[x][i];
}
return ans;
}
void _qst_Pot(int l,int r) //离散化的排序
{
int i=l,j=r,mv=Pot[(i+j)>>1].v;
while(i<=j)
{
while(Pot[i].v<mv) i++;
while(Pot[j].v>mv) j--;
if(i<=j)
{
swap(Pot[i],Pot[j]);
i++;j--;
}
}
if(l<j) _qst_Pot(l,j);
if(i<r) _qst_Pot(i,r);
}
void _qst_Seg(int l,int r)
{
int i=l,j=r,ml=Seg[(i+j)>>1].L,mr=Seg[(i+j)>>1].R;
while(i<=j)
{
while((Seg[i].L<ml)||(Seg[i].L==ml&&Seg[i].R>mr)) i++;
while((Seg[j].L>ml)||(Seg[j].L==ml&&Seg[j].R<mr)) j--;
if(i<=j)
{
swap(Seg[i],Seg[j]);
i++;j--;
}
}
if(l<j) _qst_Seg(l,j);
if(i<r) _qst_Seg(i,r);
}
void _solve()
{
_qst_Pot(0,tot);
if(Pot[0].Id&1) //重新分配区间左右端点
Seg[Pot[0].Id>>1].R=0;
else
Seg[Pot[0].Id>>1].L=0;
for(int i=1;i<=tot;i++)
{
if(Pot[i].v!=Pot[i-1].v)
Num++;
if(Pot[i].Id&1)
Seg[Pot[i].Id>>1].R=Num;
else
Seg[Pot[i].Id>>1].L=Num;
}
_qst_Seg(0,N-1);
_del();
int p=0;
for(int i=0;i<=Num;i++) //初始化倍增数组
{
while(p<=cnt)
{
if(i<=Seg[p].L)
{
f[i][0]=Seg[p].R;
break;
}
p++;
}
if(p>cnt)
f[i][0]=INF;
}
s=ceil(log2(cnt+1));
for(int j=1;j<=s;j++) //倍增计算
for(int i=0;i<=Num;i++)
f[i][j]=(f[i][j-1]==INF)?INF:f[f[i][j-1]][j-1];
for(int i=N;i<z;i++) //输出答案
_out(_query(Seg[i].L,Seg[i].R));
}
int main()
{
_init();
_solve();
return 0;
}