POJ 3667 线段树区间合并

题意:一个旅馆有n个房间,m条操作,如果输入1 b  表示住进来b个人  然后输出这b个人住的房间编号最前面的那个,如果输入 2 b c, 就是说要将b到b+c-1的房间全部全部清空。

思路:此题更新操作简单,但是查询的时候不好做,这题要添加两个数组,lsum表示一个区间从最左端开始可用的且连续的最大长度,例如区间[1,5],覆盖情况为[0,0,0,1,1],lsum=3,从最左端有3格可以利用。区间[1,5],覆盖情况为[1,0,0,0,0],lsum=0,因为从最左端开始找不到1格可用的区间rsum表示到当前节点为止的最长连续的房间个数。rsum表示一个区间从最右端开始可用的且连续的最大长度。例如区间[1,5],覆盖情况为[1,0,1,0,0],rsum=2,从最右端有2格可以利用。区间[1,5],覆盖情况为[0,0,0,0,1],rsum=0,因为从最右端开始找不到可用的区间。而sum存该区间最大的长度。

对于一个区间,我们知道它左半区间的sum,和右半区间的sum,如果左半区间的sum>=W,那么我们一定能在左边找到,所以可以到左半区间去确定该区间的具体位置,如果左端的不满足,那么我们要先考虑横跨两边的区间,一段横跨的区间,那么是左边区间的rsum+右边区间的lsum,如果满足的话,就是该区间了,它的位置也是可以确定的。如果横跨的区间不满足,那么就在右半区间找,如果右半区间的sum>=w, 那么可以在右半区间找到,所以到右半区间去确定它的具体位置。

接下来要说的就是对这三个值进行更新,当前区间的sum=max{左区间的rsum+右区间的lsum,max{左区间的sum,右区间的sum}};

如果左区间全部都可以用,那么当前的lsum=左区间lsum+右区间lsum,不然的话,就表示只有部分房间可以用,那么当前lsum=左区间lsum。

同理如果右区间全部都可以用,当前rsum=右区间rsum+左区间rsum,不然就是当前rsum=右边rsum

#include <stdio.h>
#include <algorithm>
using namespace std;

#define maxn 55555

int lsum[maxn<<2],rsum[maxn<<2],sum[maxn<<2];
int lazy[maxn<<2];
int L,R;

void pushdown(int id,int m)//向下更新。 
{
	if(lazy[id]!=-1)
	{
		lazy[id<<1]=lazy[id<<1|1]=lazy[id];
		sum[id<<1]=lsum[id<<1]=rsum[id<<1]=lazy[id]?0:m-(m>>1);//如果lazy的值不是0就归0,不然就赋值 m-(m>>1)
		sum[id<<1|1]=lsum[id<<1|1]=rsum[id<<1|1]=lazy[id]?0:m>>1;
		lazy[id]=-1;
	}
}

void pushup(int id,int m)//更新三个数组的值 
{
	lsum[id]=lsum[id<<1];
	rsum[id]=rsum[id<<1|1];
	if(lsum[id]==m-(m>>1))
	lsum[id]+=lsum[id<<1|1];
	if(rsum[id]==m>>1)
	rsum[id]+=rsum[id<<1];
	sum[id]=max(lsum[id<<1|1]+rsum[id<<1],max(sum[id<<1],sum[id<<1|1]));
}

void build(int l,int r,int id)
{
	lazy[id]=-1;
	sum[id]=lsum[id]=rsum[id]=r-l+1;
	if(l==r)return ;
	int mid=l+r>>1;
	build(l,mid,id<<1);
	build(mid+1,r,id<<1|1);
}

void updata(int c,int l,int r,int id)
{
	if(L<=l&&r<=R)
	{
		sum[id]=lsum[id]=rsum[id]=c?0:r-l+1;
		lazy[id]=c;
		return ;
	}
	pushdown(id,r-l+1);
	int mid=l+r>>1;
	if(L<=mid)updata(c,l,mid,id<<1);
	if(R>mid)updata(c,mid+1,r,id<<1|1);
	pushup(id,r-l+1);
}

int query(int w,int l,int r,int id)
{
	if(l==r)return l;
	pushdown(id,r-l+1);//这里也要pushdown ,开始漏掉WA了 
	int mid=l+r>>1;
	if(sum[id<<1]>=w)
	return query(w,l,mid,id<<1);
	else if(lsum[id<<1|1]+rsum[id<<1]>=w)//横跨区间直接返回,开始用递归的结果错了 QAQ 现在都没有想出来为什么 
	return mid-rsum[id<<1]+1;
	return query(w,mid+1,r,id<<1|1);
}

int main()
{
	int m,n,op,a,b;
	scanf("%d %d",&n,&m);
	build(1,n,1);
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d",&a);
			if(sum[1]<a)puts("0");
			else 
			{
				int p=query(a,1,n,1);
				printf("%d\n",p);
				L=p;
				R=p+a-1;
				updata(1,1,n,1);
			}
		}
		else 
		{
			scanf("%d %d",&a,&b);
			L=a;
			R=a+b-1;
			updata(0,1,n,1);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值