pku 3667 Hotel

pku 3667 Hotel

题意:

去掉题目背景,就是给你一个[1,n]的区间,初始时,整个区间为空,在区间进行俩个操作:

1) 输入 1 D :在区间上从左到右找出第一个连续的长度为D 的空间,并将该区间填满,输出区间的端点,若不存在,输出0

2) 输入 2 a b: 将区间[a,a+b-1] 填满

分析:很明显是用线段树进行维护,关键是每一个节点需要保存该区间的些什么值?

这里我们可以想到,因为要找出的是一个连续的空区间的左端点,这个区间可能分布在左儿子上,右儿子上,或者可能在俩边都有,这样给查询带来了极大的不方便。不过,我们可以充分利用线段树的结构特点,通过保存与区间左右端点有关的信息,就可以遍历整个区间的信息。

我们给每一个节点添加四个域,一个保存该区间是否整个被填满,一个域保存该区间存在的最大的连续的区间长度,一个域保存该区间以左端点开始的空区间的长度,一个域保存该区间以右端点结束的空区间的长度;

这样,我们通过维护线段树每一个域的值,就可以得到整个区间内每一个连续的空区间的长度;

维护过程头脑要异常的清醒啊!!!!

#include<iostream>
#include<algorithm>
#define MAXN 50005
using namespace std;
struct node
{
	int ls,rs,ms,c;
}p[MAXN<<2];
void PushDown(int k,int m)
{
	int kl=k<<1,kr=kl+1;
	if(p[k].c!=-1)
	{
		p[kl].c=p[kr].c=p[k].c;
		p[kl].ls=p[kl].rs=p[kl].ms= p[k].c? 0: m- (m>>1);
		p[kr].ls=p[kr].rs=p[kr].ms= p[k].c? 0: (m>>1);
		p[k].c=-1;
	}
}
void PushUp(int k,int m)//除了查询之外,难点就在于这一步维护了,将子区间的信息传递给回来
{
	int kl=k<<1,kr=kl+1;
	p[k].ls=p[kl].ls;
	p[k].rs=p[kr].rs;
	if(p[kl].ls== m-(m>>1))//若左儿子整个为空区间,则可以将右儿子的左连续空间长度加到整个空间的左连续空间长度
		p[k].ls+=p[kr].ls;
	if(p[kr].rs== m>>1)
		p[k].rs+=p[kl].rs;
	p[k].ms=max(p[kl].rs+p[kr].ls,max(p[kl].ms,p[kr].ms));//整个区间存在的最长的空区间长度等于 左连续区间,右连续区间,或中间存在的最大长度的空区间中的最大值
}
void build(int k,int s,int t)//建树
{
	p[k].ls=p[k].rs=p[k].ms=t-s+1;
	p[k].c=-1;
	if(s==t) return ;
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	build(kl,s,mid);
	build(kr,mid+1,t);
}
void update(int k,int c,int s,int t,int l,int r)//根据c的值,将区间[l,r]填满或清空
{
	if(l<=s && t<=r)
	{
		p[k].c=c;
		p[k].ms=p[k].ls=p[k].rs=c? 0: t-s+1;
		return ;
	}
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	PushDown(k,t-s+1);
	if(l<=mid) update(kl,c,s,mid,l,r);
	if(r>mid) update(kr,c,mid+1,t,l,r);
	PushUp(k,t-s+1);
}
int query(int k,int w,int s,int t)
{
	if(s==t) return s;
	PushDown(k,t-s+1);
	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
	if(p[kl].ms>=w) //左区间本身存在着长度大于w的空区间
		return query(kl,w,s,mid);
	else if(p[kl].rs+p[kr].ls>=w) //保存左右端点空区间长度的目的就在于此了,p[kl].rs+p[kr].ls 是区间内与中点有关的连续的空区间的长度
		return mid-p[kl].rs+1;
	return query(kr,w,mid+1,t);
}
int main()
{
	int n,m;
	int a,b,c;
	scanf("%d %d",&n,&m);
	build(1,1,n);
	while(m--)
	{
		scanf("%d",&c);
		if(c==1)
		{
			scanf("%d",&a);
			if(p[1].ms<a)//整个区间内的最大长度的空区间小于a
				puts("0");
			else 
			{
				b=query(1,a,1,n);
				printf("%d\n",b);
				update(1,1,1,n,b,b+a-1);
			}
		}
		else 
		{
			scanf("%d %d",&a,&b);
			update(1,0,1,n,a,a+b-1);
		}
	}
	return 0;
}
posted @ 2011-09-23 09:56  枕边梦  阅读(352)  评论(0编辑  收藏  举报