[USACO08FEB]酒店Hotel
题目描述
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.
奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。 贝西是导游,她选中了在湖边的一家著名旅馆。这家旅馆一共有N间客房,都在同一楼层, 顺序一字排开,只需要拉开窗帘,就能见到波光粼粼的湖面。
慕名而来的游客会陆续打电话到旅馆前台,要求订一段连续的房间。如果能够满足客户 的要求,旅馆总会尽量满足,但如果目前没有连续的空房,前台会向他们道歉,请顾客们另 找一家宾馆。如果有多处连续的空房,前台会挑选编号靠前优先分配。
同时,前台也会收到退房的请求,退房请求也是一段连续的房间。但有时客户会搞错, 报出的区间中有些房间本来就是空的,这不要紧,只需要把它们全部登记成空房就可以了。
你的工作,就是写一个程序,帮助前台为旅客安排房间。程序一共需要处理M条请求, 在第一个请求到来前,所有房间都是空闲的。
输入格式
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
第一行:两个用空格分开的整数:N和M,1 ≤ N ≤ 50000,1 ≤ M ≤ 50000
第二行到第M + 1行:每i + 1行描述了一个顾客的请求。每行的第一个整数Ti代表请求 的类型,如果Ti为1,表示这是一个订房请求,接下来会有一个整数Di,表示需要的区 间长度。如果Ti为2,表示这是一个退房请求,接下来会有两个整数Xi和Di,表示需要 办理退房手续的第一个房间是Xi,区间长度是Di,1 ≤ Di ≤ N,1 ≤ Xi ≤ N − Di + 1
输出格式
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.
对每个订房请求,如果这份订单可以满足,输出满足条件的第一个房间的编号,如果不 可以满足,输出0,每个请求用换行符分开。
输入样例
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
输出样例
1
4
7
0
5
【题目 https://www.luogu.org/problemnew/show/2894】
题解
题目要求满足长度的连续区间,可想到用线段树。
设0表示当前房间是空的,1表示当前房间已被订,则题目转化为求是否存在满足长度的连续的0;每个节点区间有几个0不难算,但还需要求出跨区间的0的个数,于是我们增加两个变量lx和rx,lx记录从该节点区间的左端点起连续的0的长度(是不是不好理解),例如,某节点的状态是这样的:
编号 3 4 5 6 7 8
状态 0 0 0 1 1 0
那么该节点区间从左端点起连续0的个数为三,所以这个节点的lx值为3(但愿这样能好理解一点)。
同理,rx记录的是该节点区间从右端点起连续的0的最左边的位置。
这样,跨区间的0的个数只要计算这两个区间边界起分别向左向右的连续的0的长度之和(好像又开始不好理解了)。例如,有如下两个节点:
编号 3 4 5 6 7 | 8 9 10 11
状态 0 0 1 1 0 | 0 0 1 1
易得左边节点的rx值为1,右边节点的lx值为2,那么跨这两个区间的连续的0的个数为1+2=3(这样会不会好一点);
除了线段树的基本操作,这里有个小优化
观察题目,无论是订房还是退房,都会对指定区间的每一个房间进行操作,这样很浪费时间。试想,当一个区间的房间被退后,并不会马上被用到,有可能后来又有订房操作,让房间变回原来的状态,那么我们把房间变为空房的操作就白费了,所以我们不妨偷懒一下。
我们为线段树的每一个节点添加一个标记tag,记录这个节点区间的状态,这样我们不必每次都对区间的每一个房间操作,等要用到这些房间时再把房间的状态改为标记tag的状态,同时也要改变tag的状态,以防房间多次进行重复操作。
有了tag,还需要有个过程PushDown,把当前节点的tag传给儿子节点,因为儿子节点不一定会被用到,所以不急着改变状态,先记录下来。
#include <cstdio> int lx[200005],rx[200005],mx[200005],tag[200005],n,m,p,x,d,a; int max(int x,int y) { return x>=y?x:y; } void PushDown(int l,int r,int id) { if(tag[id]==-1) return; if(tag[id]==0) lx[id]=rx[id]=mx[id]=r-l+1; else lx[id]=rx[id]=mx[id]=0; if(l<r) tag[id<<1]=tag[id<<1|1]=tag[id]; tag[id]=-1; return; } void modify(int l,int r,int id,int x,int y,int c) { if(x<=l && r<=y) { tag[id]=c; PushDown(l,r,id); return; } int mid=(l+r)>>1,ls=id<<1,rs=id<<1|1; PushDown(l,mid,ls); PushDown(mid+1,r,rs); if(x<=mid) modify(l,mid,ls,x,y,c); if(y>mid) modify(mid+1,r,rs,x,y,c); lx[id]=lx[ls]; if(lx[id]==mid-l+1) lx[id]+=lx[rs]; rx[id]=rx[rs]; if(rx[id]==r-mid) rx[id]+=rx[ls]; mx[id]=max(rx[ls]+lx[rs],max(mx[ls],mx[rs])); return; } int quary(int l,int r,int id) { int mid=(l+r)>>1,ls=id<<1,rs=id<<1|1; PushDown(l,mid,ls); PushDown(mid+1,r,rs); if(lx[id]>=d) return l; else if(mx[ls]>=d) return quary(l,mid,ls); else if(rx[ls]+lx[rs]>=d) return mid-rx[ls]+1; else return quary(mid+1,r,rs); } int main() { int i,j,k; scanf("%d%d",&n,&m); modify(1,n,1,1,n,0); for(i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d",&d); if(mx[1]<d) a=0; else a=quary(1,n,1); printf("%d\n",a); if(a>0) modify(1,n,1,a,a+d-1,1); } else { scanf("%d%d",&x,&d); modify(1,n,1,x,x+d-1,0); } } return 0; }