训练场

本文介绍了一种结合离散化与倍增技术解决区间覆盖问题的方法。通过对坐标进行离散化处理,将原始的大范围坐标转换为更小范围内的离散坐标值,进而利用倍增算法高效地求解在某一特定时间段内能同时满足多少个非重叠区间的最大数量。这种方法特别适用于处理大规模数据集中的区间查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值