CodeForces - 160E Buses and People(线段树+三维偏序)

题目链接:点击查看

题目大意:(网上复制一下别人的题意。。懒)

有n辆公交车,每辆公交车有s(起始点),f(终点),t(发车时间) (行驶不需要时间)

有m个人,每个人有l(起点),r(终点),t(出现时间)

每个人出现后会选择最早经过他且可行的公交车

(即满足s<=l,r<=f,且公交车发车时间晚于人出现时间)

输出每个人会选择那一辆公交车

如果没有符合条件的公交车输出-1

题目分析:这个题目一看到题目中的“sj ≤ liri ≤ fj and bi ≤ tj”,感觉就是偏序问题,只不过变成了三维,之前做过最简单的一维是sort排个序就行,二维的一般是给定两个区间[l,r],然后统一对某个端点排序,然后对另一个端点用线段树就能解决,现在上升到了三维,在给出区间[l,r]的基础上,还增加了一个时间t的约束,那么我们应该先对其中的一个区间端点排序,我选择了对左区间排序,然后还剩下了右区间和时间t,题目中说了t都互不相等,我们可以直接对t建线段树,让t当做下标,那么维护的权值肯定和右区间端点r有关系,那么我们需要维护什么呢?通过分析之后我们发现,如果我们已经建树后,要找满足条件(即在区间[l,r]内t最小)的t,我们在查询的时候会从根节点出发,一步一步尽量向左区间移动,因为在线段树中以t为下标建树,并且t互不相等,那么可以保证左边的t必定小于右边的t,那么尽量向左区间下移的结果就是所求答案了,我们接下来需要考虑如何在向下移动的时候,一定满足在区间[l,r]中呢,首先我们对于左区间端点l升序处理过了,可以保证当遍历到任意一个查询的时候,这个查询左区间端点之前的公交车已经被加入到线段树中了(即左区间端点l的条件已经满足),让t满足条件并且取最小可以在查询的时候边判断边下传,可以保证下传的时候都是在满足t的范围内(即公交车的t要小于人的t)下传的,这样我们就可以想到,能不能在线段树中维护右区间端点的最大值来让每次下传都满足条件呢,每次只要下传的时候判断一下右区间是否在维护的最大值的范围内即可轻易满足右区间的条件,因为在某一个时间t的时候,公交车可以开到最大的右端点,那么只要让所求的右端点小于这个最大值,就能取到这个时间t,从而同时满足了l和t的约束,这样一来因为每个时间t都是互不相同的,所以可以通过线段树来解决这个三维偏序问题。

同样需要注意一下,t的范围过大,需要离散化一下,这里不多赘述了,上代码吧

#include
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int inf=0x3f3f3f3f;

const int N=1e6+100;

vectorv;

int ans[N];

struct qu
{
	int l,r,t,id;
	bool operator<(qu a)const
	{
		return l>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void update(int k,int pos,int val,int id)
{
	if(tree[k].l==tree[k].r)
	{
		tree[k].mmax=val;
		tree[k].id=id;
		return;
	}
	int mid=tree[k].l+tree[k].r>>1;
	if(mid>=pos)
		update(k<<1,pos,val,id);
	else
		update(k<<1|1,pos,val,id);
	pushup(k);
}

int query(int k,int num,int b)
{
	if(tree[k].mmax>1;
	int ans=-1;
	if(mid>=b)
	{
		ans=query(k<<1,num,b);
		if(ans>0)
			return ans;
	}
	return query(k<<1|1,num,b);
}

int getid(int num)//离散化:获得新编号
{
	return lower_bound(v.begin(),v.end(),num)-v.begin()+1;
}

int main()
{
//	freopen("input.txt","r",stdin);
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		v.clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&qu1[i].l,&qu1[i].r,&qu1[i].t);
			v.push_back(qu1[i].t);
			qu1[i].id=i;
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d",&qu2[i].l,&qu2[i].r,&qu2[i].t);
			qu2[i].id=i;
			v.push_back(qu2[i].t);
		}
		sort(v.begin(),v.end());//离散化:排序
		v.erase(unique(v.begin(),v.end()),v.end());//离散化:去重
		sort(qu1+1,qu1+1+n);//对公交车的左区间端点排序
		sort(qu2+1,qu2+1+m);//对人的左区间端点排序
		int cnt=1;
		build(1,1,v.size());//建树,这里一定要记着,线段树是要用离散化后t的个数来建树,一开始习惯性的用n建树,结果连样例都过不去。。自闭
		for(int i=1;i<=m;i++)
		{
	/*		if(qu1[cnt].l>qu2[i].l)//不知道为什么加了这么一段剪枝会WA。。(可能是我太菜了)
			{
				ans[qu2[i].id]=-1;
				continue;
			}*/
			while(qu1[cnt].l<=qu2[i].l&&cnt<=n)//每次先把左区间人左边的公交车全部加入线段树中
			{
				update(1,getid(qu1[cnt].t),qu1[cnt].r,qu1[cnt].id);
				cnt++;
			}
			ans[qu2[i].id]=query(1,qu2[i].r,getid(qu2[i].t));
		}
		for(int i=1;i<=m;i++)
			printf("%d ",ans[i]);
		cout<

 

你可能感兴趣的:(线段树,线段树,偏序问题)