poj3667 Hotel (线段树区间合并)

链接:http://poj.org/problem?id=3667

题意:n个连续的房间m个操作。操作分两种,第一种以1 x形式给出,找到最左的能连续容下x个人的连续房间,并输出左端点的编号,如果找不到就输出0.第二种以2 l x的形式给出,表示以l为起点的x个房间都清空。


查询的时候要能直接获取区间的最大连续空房间,这样就能判断能不能连续放下这x个人,但这样还确定不了具体放哪。放的位置有三种情况1.放在左儿子那个区间。2.放在右儿子那个区间。3.放在左右儿子中间,就是占用左区间的右部分和右区间的左部分。

这样就需要用sum保存整个区间最大的连续段,lsum保存从左端开始的最大连续段,rsum保存右端开始的最大连续段

用一个标记mark表示区间状态,0:区间全空。1:区间全满。-1:当前区间已初始化,不用向下更新


向下更新策略:先向下转移标记mark,在通过标记更新相应的数据。

向上更新策略:sum=max(左儿子的sum,右儿子的sum,左右儿子中间部分)

lsum=左儿子的lsum,如果左儿子为空还要加上右儿子的lsum

rsum=右儿子的rsum,如果右儿子为空还要加上左儿子的rsum


#include<stdio.h>
#include<string.h>
#define MAXN 50005
struct node
{
	int l,r;
	int mark;
	int lsum,rsum,sum;
	int getlen() {return r-l+1;}
	void getsum() {lsum=rsum=sum=(mark?0:getlen());}
}t[MAXN*4];
int max(int a,int b) {return a>b?a:b;}
void construct(int l,int r,int p)
{
	t[p].l=l,t[p].r=r;
	t[p].lsum=t[p].rsum=t[p].sum=t[p].getlen();
	t[p].mark=-1;
	if(l==r) return ;
	int m=(l+r)>>1;
	construct(l,m,p*2);
	construct(m+1,r,p*2+1);
}
int query(int x,int p)
{
	if(t[p].l==t[p].r&&x==1) return t[p].l;//单个节点特判
	if(t[p].mark!=-1)
	{
		t[p*2].mark=t[p*2+1].mark=t[p].mark;
		t[p].mark=-1;
		t[p*2].getsum();
		t[p*2+1].getsum();
	}
	if(x<=t[p*2].sum) return query(x,p*2);
	else if(t[p*2].rsum+t[p*2+1].lsum>=x) return t[p*2].r-t[p*2].rsum+1;
	else if(x<=t[p*2+1].sum) return query(x,p*2+1);
	else return 0;
}
void modify(int l,int r,int val,int p)
{
	if(t[p].l==l&&t[p].r==r)
	{
		t[p].mark=val;
		t[p].getsum();
		return ;
	}
	if(t[p].mark!=-1)
	{
		t[p*2].mark=t[p*2+1].mark=t[p].mark;
		t[p].mark=-1;
		//每次更新了mark标记都要重新获取长度
		t[p*2].getsum();
		t[p*2+1].getsum();
	}
	int m=(t[p].l+t[p].r)>>1;
	if(r<=m) modify(l,r,val,p*2);
	else if(l>m) modify(l,r,val,p*2+1);
	else {modify(l,m,val,p*2);modify(m+1,r,val,p*2+1);}

	int temp=max(t[p*2].sum,t[p*2+1].sum);
	t[p].sum=max(temp,t[p*2].rsum+t[p*2+1].lsum);

	t[p].lsum=t[p*2].lsum;
	t[p].rsum=t[p*2+1].rsum;

	if(t[p*2].sum==t[p*2].getlen()) t[p].lsum+=t[p*2+1].lsum;
	if(t[p*2+1].sum==t[p*2+1].getlen()) t[p].rsum+=t[p*2].rsum;
}
int main()
{
	int op,l,x,n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		construct(1,n,1);
		while(m--)
		{
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d",&x);
				int flag=query(x,1);
				printf("%d\n",flag);
				if(flag) modify(flag,flag+x-1,1,1);
			}
			else
			{
				scanf("%d%d",&l,&x);
				modify(l,l+x-1,0,1);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值