poj(3667)——hotel(线段树区间合并)

题目的大致意思是现在有一个宾馆,然后里面共有n个房间,然后有m次询问,每次询问都有两种形式:

1: 那么代表的是现在要搬进来x个人,然后他们是有要求的,他们需要满足他们x个人都是要连续的住在一起,如果没有这种情况的话,那么就输出0,否则输出满足条件的最左边的那个区间;

2: 输入a,b,分别代表的是从a这房间开始,搬出去b个人;

这道题一开始拿到题时并没有什么思路,在网上看了找了许久,找到了两篇比较好的文章(因为大多数都是鱼龙混杂,贴个代码就完事了==b),在这里附上链接,供大家参考。

http://www.2cto.com/kf/201402/278724.html

http://blog.csdn.net/piaoyi0208/article/details/8149804     (我觉得第二篇写的挺好的,因为你可以学习一下他代码中的思路,然后差不多就可以懂了)

思路是:

首先我们设六个标记(有人说只要4个就可以了),分别为l,r(它们代表的是区间),flag(代表的是这个区间是否被标记过,如果为1,说明这个flag所对应的区间已经满了,需要被清空,如果为0,说明flag所对应的区间是空的或者说有人已经搬出去了,如果是-1,当然我们对flag的初始化也是为-1的,就说明不用进行更新操作)。sum(代表的是它所对应的区间含有的房间的总数)。ls与rs(ls代表这个区间从左边开始连续区间的个数,rs代表的是从右边开始连续区间的个数)。

然后我们讲一下pushup这个函数的作用(当然,顾名思义,当然是向上更新的意思),首先当前结点的ls肯定是从左区间中来的,这个画一下图就可以知道了,rs那么是从右区间中来的,然后当当前区间的总ls==mid-tree[v].l+1时,那么就说明我们当前结点的ls还可以扩大的,那么我们还可以加上右儿子的rs,这样就扩大了区间。同理下面一句也是一样的意思。当然,最后,我们还要修改一下tree[v].sum的最大值,它是左右儿子与左儿子的rs加上右儿子的ls中的最大值。

void pushup(int v){
	int temp=v<<1;
	tree[v].ls=tree[temp].ls;
	tree[v].rs=tree[temp+1].rs;
	int mid=(tree[v].l+tree[v].r)>>1;
	if(tree[v].ls==mid-tree[v].l+1)  tree[v].ls+=tree[temp+1].ls;
	if(tree[v].rs==tree[v].r-mid) tree[v].rs+=tree[temp].rs;
	tree[v].sum=max(max(tree[temp].sum,tree[temp+1].sum),tree[temp].rs+tree[temp+1].ls);
}

然后是pushdown函数,这里是lazy思想的核心,当flag标记不是-1时,那么我们就要往下更新,首先给下面的子节点也都带上flag标记,然后再分类讨论flag的情况,当flag为1的时候,说明已经全都满了,那么要全部清空,要不就是flag为0,说明可以清空了,那么把区间的长度再重新标记为原先的长度。当然最重要的是不要忘记把父节点的flag重新置为-1.

void pushdown(int v){
	int temp=v<<1;
	if(tree[v].flag!=-1){
		tree[temp].flag=tree[temp+1].flag=tree[v].flag;
		if(tree[v].flag){
			tree[temp].sum=tree[temp].ls=tree[temp].rs=0;
			tree[temp+1].sum=tree[temp+1].ls=tree[temp+1].rs=0;
		}
		else{
			tree[temp].sum=tree[temp].ls=tree[temp].rs=tree[temp].r-tree[temp].l+1;
			tree[temp+1].sum=tree[temp+1].ls=tree[temp+1].rs=tree[temp+1].r-tree[temp+1].l+1;
		}
	}
	tree[v].flag=-1;
}

至于其他的,如果敲过线段树的其他题目的话,那么也应该是比较好理解的。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 55555
int pos=0;
struct node{
	int l,r,flag;
	int sum,ls,rs;
}tree[maxn*4];
void pushup(int v){
	int temp=v<<1;
	tree[v].ls=tree[temp].ls;
	tree[v].rs=tree[temp+1].rs;
	int mid=(tree[v].l+tree[v].r)>>1;
	if(tree[v].ls==mid-tree[v].l+1)  tree[v].ls+=tree[temp+1].ls;
	if(tree[v].rs==tree[v].r-mid) tree[v].rs+=tree[temp].rs;
	tree[v].sum=max(max(tree[temp].sum,tree[temp+1].sum),tree[temp].rs+tree[temp+1].ls);
}
void pushdown(int v){
	int temp=v<<1;
	if(tree[v].flag!=-1){
		tree[temp].flag=tree[temp+1].flag=tree[v].flag;
		if(tree[v].flag){
			tree[temp].sum=tree[temp].ls=tree[temp].rs=0;
			tree[temp+1].sum=tree[temp+1].ls=tree[temp+1].rs=0;
		}
		else{
			tree[temp].sum=tree[temp].ls=tree[temp].rs=tree[temp].r-tree[temp].l+1;
			tree[temp+1].sum=tree[temp+1].ls=tree[temp+1].rs=tree[temp+1].r-tree[temp+1].l+1;
		}
	}
	tree[v].flag=-1;
}
void build(int l,int r,int v){
	tree[v].l=l;
	tree[v].r=r;
	tree[v].sum=tree[v].ls=tree[v].rs=r-l+1;
	tree[v].flag=-1;
	if(l==r) return;
	int mid=(l+r)>>1;
	int temp=v<<1;
	build(l,mid,temp);
	build(mid+1,r,temp+1);
}
int query(int l,int r,int v,int x){
	if(l==r){
		return l;
	}
	pushdown(v);
	int mid=(tree[v].l+tree[v].r)>>1;
	int temp=v<<1;
	if(tree[temp].sum>=x) return query(l,mid,temp,x);
	else if(tree[temp].rs+tree[temp+1].ls>=x) return mid-tree[temp].rs+1;
	else return query(mid+1,r,temp+1,x);
}
void update(int l,int r,int v,int cnt){
	if(tree[v].l==l&&r==tree[v].r){
		tree[v].flag=cnt;
		if(cnt){
			tree[v].sum=tree[v].ls=tree[v].rs=0;
		}
		else{
			tree[v].sum=tree[v].ls=tree[v].rs=tree[v].r-tree[v].l+1;
		}
		return;
	}
	pushdown(v);
	int mid=(tree[v].l+tree[v].r)>>1;
	int temp=v<<1;
	if(r<=mid) update(l,r,temp,cnt);
	else if(l>mid) update(l,r,temp+1,cnt);
	else{
		update(l,mid,temp,cnt);
		update(mid+1,r,temp+1,cnt);
	}
	pushup(v);
}
int main(){
	int n,m;
	int x,q,a,b;
	scanf("%d%d",&n,&m);
	build(1,n,1);
	while(m--){
		scanf("%d",&q);
		if(q==1){
			scanf("%d",&x);
			if(tree[1].sum<x){
				printf("0\n");
				continue;
			}
			pos=0;
			pos=query(1,n,1,x);
			printf("%d\n",pos);
			update(pos,pos+x-1,1,1);
		}
		else{
			scanf("%d%d",&a,&b);
			update(a,a+b-1,1,0);	
		}
	}
}

总之,挺不错的一道线段树的题目,希望能够多多思考,举一反三,加油!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值