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
,接下来有m
行,每行第一个数op
是操作,当操作值为1
的时候,说明现在有一个查询,第二个数k
代表要在旅馆要开连续的k
个房间,如果可以开房,那就输出这k
个房间的第一个房间的位置,如果旅馆住不下就输出0
.当操作数op
为2
的时候,接下来给出两个数l,r
代表一个区间以l
为起点,从l
从左往右数r
个位置的人退房,也就是清空这个区间。
首先暴力不可取,利用线段树做,当时学线段树的时候太懒了,没学区间合并,导致今天凉了,所以还是要补一补相关知识…
因为是区间问题,所以肯定要用到lazy
标记,还需要额外三个数组:
- msum[rt]:当前端点代表的区间最大连续空房间的个数
- lsum[rt]:当前端点代表的区间从左到右最大连续空房间的个数
- rsum[rt]:当前端点代表的区间从右到左最大连续空房间的个数
- cover[rt]:lazy标记,-1代表不操作,0代表此区间置空,1代表置满
先说明cover
标记一共有3个值,-1
代表没有操作,1
代表要住人,0
代表退房
在更新的时候可以通过c
的值来直接对rsum,lsum,msum
进行赋值,还要给相关节点打上lazy
标记。先向下更新,在回溯的时候向上更新,向下更新的时候和普通的线段树基本一样,在向上更新的时候要注意,先更新当前端点从左到右的左子树和从右到左的右子树,当lsum
左子树区间住满的时候,证明还可以住人,就加上它右子树区间的值,当rsum
的右子树区间住满的时候,就可以加上左子树区间的值
查询的时候,先找msum
左儿子是否足够,然后如果不够就找lsum
左儿子的右区间和rsum
右儿子的左区间的和是否足够,如果两个都不够的话就找右儿子(这个时候右儿子就肯定满足了)。
具体看代码…
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const int N=50000+20;
int lsum[N<<2],rsum[N<<2],msum[N<<2],cover[N<<2];
/*
msum[rt]:当前端点代表的区间最大连续空房间的个数
lsum[rt]:当前端点代表的区间从左到最大连续空房间的个数
rsum[rt]:当前端点代表的区间从右到左最大连续空房间的个数
cover[rt]:lazy标记,-1代表不操作,0代表此区间置空,1代表置满
*/
void pushup(int rt,int m)//向上更新
{
//更新当前端点从左到右的左子树和从右到左的右子树
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
if(lsum[rt]==m-(m>>1))//当左子树区间住满
lsum[rt]+=lsum[rt<<1|1];
if(rsum[rt]==(m>>1))//当右子树区间住满
rsum[rt]+=rsum[rt<<1];
//这个大区间连续的个数为:这个区间的左右子树的最大连续长度和从左到右的右子树+从右到左的左子树
msum[rt]=max(lsum[rt<<1|1]+rsum[rt<<1],max(msum[rt<<1],msum[rt<<1|1]));
}
void pushdown(int rt,int m)
{
if(~cover[rt])//当存在延迟标记
{
cover[rt<<1]=cover[rt<<1|1]=cover[rt];
msum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=cover[rt]?0:m-(m>>1);
msum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=cover[rt]?0:(m>>1);
cover[rt]=-1;
}
}
void build(int l,int r,int rt)
{
cover[rt]=-1;
lsum[rt]=rsum[rt]=msum[rt]=r-l+1;
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
msum[rt]=lsum[rt]=rsum[rt]=c?0:r-l+1;//如果要增加人,就变成0,如果是消去人,就变成区间的长度
cover[rt]=c;//进行lazy标记
return;
}
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m) update(L,R,c,lson);
if(R>m) update(L,R,c,rson);
pushup(rt,r-l+1);
}
int query(int w,int l,int r,int rt)//查询能放下w个位置的最左端点
{
if(l==r) return l;
pushdown(rt,r-l+1);
int m=(l+r)>>1;
if(msum[rt<<1]>=w)
return query(w,lson);
else if(rsum[rt<<1]+lsum[rt<<1|1]>=w)//从右到左连续的左子树 +从左到右连续的右子树
return m-rsum[rt<<1]+1;
else
return query(w,rson);
}
int main()
{
int n,m,op,k,a,b;
while(~scanf("%d%d",&n,&m))
{
build(1,n,1);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&k);
if(msum[1]<k)//当整个区间连续房间数小于k时
puts("0");
else
{
int p=query(k,1,n,1);//查询最小的房间位置
printf("%d\n",p);
update(p,p+k-1,1,1,n,1);//更新区间[p,p+k-1]
}
}
else if(op==2)
{
scanf("%d%d",&a,&b);
update(a,a+b-1,0,1,n,1);//更新区间[a,a+b-1]
}
}
}
return 0;
}