poj 3667 hotel

本文分享了一道关于线段树区间合并的问题解决过程。通过实际案例,详细介绍了线段树的基本构造、更新及查询操作,并针对特定问题进行了优化。

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

搞了一个下午,终于搞定了,这题是线段树的区间合并!

以前有做过类似的题目,所以还是有点感觉的!

在统计区间的时候,看了一下别人的统计方法!

终于写出来的!呵呵!!

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Max 50005
struct node
{
	int l,r;
	int rsum,lsum;//从最左端连续的区间数,从右端到末端的区间数
	int sum;//这个区间的空房间总数
	int flag;//-1代表区间的有空的,也有不空,1代表房间已满,0代表这段区间的房间是空的
}tree[4*Max];
int max(int a,int b)
{
	if(a>b)
		return a;
	else
		return b;
}
int ans,a,b;
void build(int step,int l,int r)
{
	tree[step].l=l;
	tree[step].r=r;
	tree[step].flag=0;
	tree[step].sum=tree[step].lsum=tree[step].rsum=r-l+1;
	if(l==r)
		return ;
	int mid=(l+r)>>1;
	build(step<<1,l,mid);
	build(step<<1|1,mid+1,r);
}
void updata(int step,int l,int r,int x)
{
	if(tree[step].l==l&&tree[step].r==r)
	{
		tree[step].flag=x;
		if(x==1)
		{
			tree[step].sum=tree[step].lsum=tree[step].rsum=0;
		}
		else
		{
			tree[step].sum=tree[step].lsum=tree[step].rsum=tree[step].r-tree[step].l+1;
		}
		return ;
	}
	if(tree[step].flag!=-1)//传递父亲的性质
	{
		tree[step<<1].flag=tree[step<<1|1].flag=tree[step].flag;
		if(tree[step].flag==0)
		{
			tree[step<<1].sum=tree[step<<1].lsum=tree[step<<1].rsum=tree[step<<1].r-tree[step<<1].l+1;
			tree[step<<1|1].sum=tree[step<<1|1].lsum=tree[step<<1|1].rsum=tree[step<<1|1].r-tree[step<<1|1].l+1;
		}
		else
		{
			tree[step<<1].sum=tree[step<<1].lsum=tree[step<<1].rsum=0;
			tree[step<<1|1].sum=tree[step<<1|1].lsum=tree[step<<1|1].rsum=0;
		}
		tree[step].flag=-1;
	}
	int mid=(tree[step].l+tree[step].r)>>1;
	if(r<=mid)
	{
		updata(step<<1,l,r,x);
	}
	else
	{
		if(l>mid)
		{
			updata(step<<1|1,l,r,x);
		}
		else
		{
			updata(step<<1,l,mid,x);
			updata(step<<1|1,mid+1,r,x);
		}
	}
	//合并区间,把孩子的性质合并给父亲
	tree[step].lsum=tree[step<<1].lsum;
	tree[step].rsum=tree[step<<1|1].rsum;
	tree[step].sum=max(max(tree[step<<1].sum,tree[step<<1|1].sum),tree[step<<1].rsum+tree[step<<1|1].lsum);//取左孩子的区间数,右孩子区间数,左右区间合并起来的区间数的最大值
	if(tree[step].lsum==(tree[step<<1].r-tree[step].l+1))//合并后,如果左孩子整个区间跨约右孩子
		tree[step].lsum+=tree[step<<1|1].lsum;
	if(tree[step].rsum==(tree[step<<1|1].r-tree[step<<1|1].l+1))//部分的左孩子跨越整个右孩子
		tree[step].rsum+=tree[step<<1].rsum;
}
int compute(int step)
{
	int m=(tree[step].r+tree[step].l)>>1;
	if(tree[step].flag==0&&tree[step].sum>=a)
		return tree[step].l;
		if(tree[step].flag==-1)//含有多个区间段,有空,有不空
		{
			if(tree[step<<1].sum>=a)//优先考虑左孩子
			{
				return compute(step<<1);
			}
			else
				if((tree[step<<1].rsum+tree[step<<1|1].lsum)>=a)//中间的区域
					return m-tree[step<<1].rsum+1;
				else
					return compute(step<<1|1);//右孩子
		}
		return -1;
}
int main()
{
	int n,m,op;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d",&a);
			if(tree[1].sum<a)
			{
				printf("0\n");
				continue;
			}
			ans=compute(1);
			printf("%d\n",ans);
			updata(1,ans,ans+a-1,1);
		}
		else
		{
			scanf("%d%d",&a,&b);
			updata(1,a,a+b-1,0);
		}
	}
	return 0;
}


			


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值