POJ[3667] Hotel【线段树区间合并+延迟标记】

Description


The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their vacation residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located on the same side of an extremely long hallway (all the better to see the lake, of course).The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N) and approach the front desk to check in. Each group i requests a set of Di contiguous rooms from Canmuu, the moose staffing the counter. He assigns them some set of consecutive room numbers r..r+Di-1 if they are available or, if no contiguous set of rooms is available, politely suggests alternate lodging. Canmuu always chooses the value of r to be the smallest possible.Visitors also depart the hotel from groups of contiguous rooms. Checkout i has the parameters Xi and Di which specify the vacating of rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms might be empty before the checkout.Your job is to assist Canmuu by processing M (1 ≤ M < 50,000) checkin/checkout requests. The hotel is initially unoccupied.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a check-in request: 1 and Di (b) Three space-separated integers representing a check-out: 2, Xi, and Di 

Output

* Lines 1.....: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms to be occupied. If the request cannot be satisfied, output 0.

Sample Input

10 6
1 3
1 3
1 3
1 3
2 5 5
1 6

Sample Output

1
4
7
0
5

题目大意:
有一家旅馆,有N个房间,一开始全空。之后有M次操作,操作包括两种:输入1 D,表示要入住入D个人,要求分配的房间号为连续的。如果存在空的连续的房间,输出满足条件的最小的房间号,否则输出0(即没有连续的D个房间);输入2 X D,表示房间号为X...X+D+1的人均离开。

分析:
题目涉及区间操作,考虑用线段树建立模型。线段树的节点内用sum维护该区间内的最大连续空闲区间的长度,lsum维护区间 [l,r] 从l开始向右的连续空闲区间的长度,rsum维护区间 [l,r]从r开始向左的连续空闲区间的长度(便于维护跨越节点区间的区间)。利用延迟标记减少更新深度。当客人离开时,向上合并空闲区间。

具体解释见代码。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <cstdio> 
#include <map> 
#include <iomanip>

#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define INF 0x3f3f3f3f

using namespace std;

struct segtree{
	int l,r;
	int lsum,rsum,sum;//lsum从左端开始连续的区间长度,rsum从右端开始连续的区间长度 
	int tag;//延迟标记 
};

segtree t[50005*4];

void build(int p,int l,int r){//建树 
	int mid=(l+r)/2;
	t[p].l=l;
	t[p].r=r;
	t[p].lsum=t[p].rsum=t[p].sum=r-l+1;
	t[p].tag=-1;
	if(l==r)  return;
	build(p*2,l,mid);
	build(2*p+1,mid+1,r);
}

void pushdown(int p){//将父节点的状态向下传递(延迟标记的传递) 
	if(t[p].tag!=-1){//说明父节点的状态已被改变 
		int lson=p*2,rson=p*2+1;
		t[lson].tag=t[rson].tag=t[p].tag;
		if(t[p].tag==1){//父节点的区间被占用 
			t[lson].lsum=t[lson].rsum=t[lson].sum=0;
			t[rson].lsum=t[rson].rsum=t[rson].sum=0;
		}
		else{//父节点的区间空闲 
			t[lson].lsum=t[lson].rsum=t[lson].sum=(t[lson].r-t[lson].l+1);
			t[rson].lsum=t[rson].rsum=t[rson].sum=(t[rson].r-t[rson].l+1);
		}
		t[p].tag=-1;//父节点传递完成后标志复原 
	}
}

int query(int len,int p){
	if(t[p].l==t[p].r){
		return t[p].l;
	}
	pushdown(p);//首先将本节点的状态传递下去,便于接下来的递归查询 
	int lson=p*2;
	int rson=p*2+1;
	if(t[lson].sum>=len){//如果左侧的空闲长度足够,先检测左侧(保证最小的编号) 
		return query(len,lson);
	}
	else if(t[lson].rsum+t[rson].lsum>=len){//考虑跨越左右两侧的区间 
		return (t[p].l+t[p].r)/2-t[lson].rsum+1;
	} 
	else{//检测右侧 
		return query(len,rson);
	} 
} 

void pushup(int p){//区间合并 
	int lson=p*2,rson=p*2+1;
	t[p].lsum=t[lson].lsum;
	t[p].rsum=t[rson].rsum;
	//进一步更新lsum和rsum 
	if(t[lson].lsum==(t[lson].r-t[lson].l+1))  t[p].lsum+=t[rson].lsum;
	if(t[rson].rsum==(t[rson].r-t[rson].l+1))  t[p].rsum+=t[lson].rsum;
	//sum取三种情况下的最大值 
	t[p].sum=max(t[lson].rsum+t[rson].lsum,max(t[lson].sum,t[rson].sum));
}

void update(int p,int L,int R,int flag){//flag=1表示占用,flag=0表示空闲 
	int lson=p*2,rson=p*2+1;
	if(L<=t[p].l&&R>=t[p].r){//区间被完全覆盖 
		t[p].tag=flag;
		t[p].lsum=t[p].rsum=t[p].sum=(flag?0:(t[p].r-t[p].l+1));
		return;//由于此处直接返回,所以需要延迟标记,此处未更新该节点的子树 
	}
	pushdown(p);//进入子节点前先传递延迟标记,便于接下来的递归更新 
	int mid=(t[p].l+t[p].r)/2;
	if(L<=mid){
		update(lson,L,R,flag);
	} 
	if(R>mid){
		update(rson,L,R,flag);
	}
	pushup(p);//子节点更新返回后要更新父节点 
}



int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	rep(i,1,m){
		int com;
		scanf("%d",&com);
		if(com==1){
			int d;
			scanf("%d",&d);
			if(d>t[1].sum)  printf("0\n");//如果需要入住的量大于整个区间内的连续空闲区间,则无法安排 
			else{
				int ans=query(d,1);
				printf("%d\n",ans);
				update(1,ans,ans+d-1,1);//记得要更新 
			}
		}
		else{
			int x,d;
			scanf("%d%d",&x,&d); 
			update(1,x,x+d-1,0);
		}
	} 
	return 0;
}
发布了30 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/qq_42840665/article/details/96893844