题意
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 21672 | Accepted: 9441 |
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
Source
n个连续的房间m个操作。操作分两种,第一种以1 x形式给出,找到最左的能连续容下x个人的连续房间,并输出左端点的编号,如果找不到就输出0.第二种以2 l x的形式给出,表示以l为起点的x个房间都清空
分析
参照yjCola的题解。
查询的时候要能直接获取区间的最大连续空房间,这样就能判断能不能连续放下这x个人,但这样还确定不了具体放哪。放的位置有三种情况1.放在左儿子那个区间。2.放在右儿子那个区间。3.放在左右儿子中间,就是占用左区间的右部分和右区间的左部分。
这样就需要用sum保存整个区间最大的连续段,lsum保存从左端开始的最大连续段,rsum保存右端开始的最大连续段
用一个标记mark表示区间状态,1:区间全空。0:区间全满。-1:当前区间已初始化,不用向下更新
向下更新策略:先向下转移标记mark,在通过标记更新相应的数据。
向上更新策略:sum=max(左儿子的sum,右儿子的sum,左右儿子中间部分)
lsum=左儿子的lsum,如果左儿子为空还要加上右儿子的lsum
rsum=右儿子的rsum,如果右儿子为空还要加上左儿子的rsum
时间复杂度(O(m log n))
代码
线段树节点上存储区间端点信息在向上合并的时候比较方便。
#include<iostream>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
co int N=2e5;
int n,m;
struct T{
int l,r,ls,rs,s,p;
}t[N];
#define lc (p<<1)
#define rc (p<<1|1)
void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
t[p].s=t[p].ls=t[p].rs=r-l+1;
t[p].p=-1;
if(l==r) return;
int mid=l+r>>1;
build(lc,l,mid),build(rc,mid+1,r);
}
void spread(int p){
t[lc].p=t[rc].p=t[p].p;
if(t[lc].p)
t[lc].ls=t[lc].rs=t[lc].s=t[lc].r-t[lc].l+1,
t[rc].ls=t[rc].rs=t[rc].s=t[rc].r-t[rc].l+1;
else
t[lc].ls=t[lc].rs=t[lc].s=0,
t[rc].ls=t[rc].rs=t[rc].s=0;
t[p].p=-1;
}
void change(int p){
t[p].ls=t[lc].ls,t[p].rs=t[rc].rs;
if(t[lc].ls==t[lc].r-t[lc].l+1) t[p].ls+=t[rc].ls;
if(t[rc].rs==t[rc].r-t[rc].l+1) t[p].rs+=t[lc].rs;
t[p].s=std::max(std::max(t[lc].s,t[rc].s),t[lc].rs+t[rc].ls);
}
int ask(int p,int d){
if(t[p].l==t[p].r) return t[p].l;
if(t[p].p!=-1) spread(p);
if(t[lc].s>=d) return ask(lc,d);
int mid=t[p].l+t[p].r>>1;
if(t[lc].rs+t[rc].ls>=d) return mid-t[lc].rs+1;
if(t[rc].s>=d) return ask(rc,d);
return -1;
}
void change1(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r) return t[p].ls=t[p].rs=t[p].s=t[p].p=0,void();
if(t[p].p!=-1) spread(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid) change1(lc,l,r);
if(r>mid) change1(rc,l,r);
change(p);
}
void change2(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r) return t[p].ls=t[p].rs=t[p].s=t[p].r-t[p].l+1,t[p].p=1,void();
if(t[p].p!=-1) spread(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid) change2(lc,l,r);
if(r>mid) change2(rc,l,r);
change(p);
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n),read(m);
build(1,1,n);
for(int x,d,ans;m--;){
if(read<int>()==1){
read(d);
if(t[1].s<d) puts("0");
else{
printf("%d
",ans=ask(1,d));
change1(1,ans,ans+d-1);
}
}
else{
read(x),read(d);
change2(1,x,x+d-1);
}
}
return 0;
}