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.
gdgzoi上的中文版题目描述:
这大概是全网上第一篇分块的题解吧!
蒟蒻博主刚刚去学了一下分块,于是就突发奇想用分块来写这道线段树区间合并的题目。更主要的原因是蒟蒻博主开始时怎么都想不到怎么用线段树来维护,还想了很久树套树,可持久化XXX,splay什么的。
好,进入正题。怎么用分块维护呢?
对于每个房间,维护四个东西:pre,pred,sub,subd,分别代表:这个房间左边的最靠近的非空房间的位置,这个房间左边的最靠近的非空房间到它的距离,这个房间右边边的最靠近的非空房间的位置,这个房间右边的最靠近的非空房间到它的距离。
对于每个块,块的大小设置为sqrt(n)左右,维护一个东西:maxn,代表这个块中每个房间到右边一个最近的非空房间的距离的最大值,即sub[i]-i或者subd[i]的最大值。
可以发现,若对于一个房间,sub[i]-i>p或者subd[i]>p,则从i+1起就有连续的p个房间。 为了方便,我在0和n+1的位置加了两个虚点。
对于操作一,查询连续的p个房间,就从左到右遍历所有的块,若一个块的maxn>p,则这个块中一定有满足条件的房间,然后再从左到右遍历一遍这个块找到那个房间即可。接着,设找到的这p间房左端点为l,右端点为r,维护pre,sub,pred,subd的值,把pre[l]~l-1的sub值设置为l,把r+1~sub[r]的pre值设置为r。若p>1,则再将l~r-1的subd值设置为1,把l+1~r的pred值设置为1。修改时,对于两边的两个不完整的块暴力修改,中间跨过的完整的块打标记。不要忘记维护maxn和标记下传。
对于操作二,就是把l~r区间全部退房。维护pre和sub的方法就是把pre[l]~r的sub值都设置为sub[r],把l~sub[r]的pre值设置为pre[l],修改的方法与上文同理。
可以发现,pre和pred,同时只会用到一个,sub和subd也同时只会用到一个,于是蒟蒻博主偷懒只开了两个数组pre和sub处理,当pre[i]>=0时表示的是pre,否则表示的是-pred,也就是把pred变成负数用于区分。sub[i]同理。这直接降低了代码可读性。
时间复杂度:由于块有sqrt(n)块,每块的大小为sqrt(n),所以每次操作的时间为sqrt(n),总时间复杂度为O(msqrt(n))。由于极端的操作较少,所用的时间远远小于(msqrt(n)),大概是wyc的线段树区间合并O(mlogn)的1.5倍。
233,就这样!
可读性非常差而且非常疏松的SpicyChicken代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50005,inf=0x7fffffff;
int n,m,op,x,p,pr,su,ans,size,bel[N],pre[N],sub[N],tag1[N],tag2[N],maxn[N];
inline int rd(){
register char ch=getchar();
register int ret=0;
while(ch<'0'||ch>'9'){
ch=getchar();
}
while(ch>='0'&&ch<='9'){
ret=ret*10+ch-'0';
ch=getchar();
}
return ret;
}
void setsub(int l,int r,int x){
if(tag1[bel[l]]!=-inf){
for(int i=size*bel[l],up=min(size*(bel[l]+1),n+1);i<up;i++){
sub[i]=tag1[bel[l]];
}
tag1[bel[l]]=-inf;
}
for(int i=l,up=min(r,size*(bel[l]+1)-1);i<=up;i++){
sub[i]=x;
}
maxn[bel[l]]=0;
for(int i=size*bel[l],up=min(size*(bel[l]+1),n+1);i<up;i++){
if(sub[i]>=0){
maxn[bel[l]]=max(maxn[bel[l]],sub[i]-i);
}else{
maxn[bel[l]]=max(maxn[bel[l]],-sub[i]);
}
}
if(bel[l]==bel[r]){
return;
}
if(tag1[bel[r]]!=-inf){
for(int i=size*bel[r],up=min(size*(bel[r]+1),n+1);i<up;i++){
sub[i]=tag1[bel[r]];
}
tag1[bel[r]]=-inf;
}
for(int i=size*bel[r];i<=r;i++){
sub[i]=x;
}
maxn[bel[r]]=0;
for(int i=size*bel[r],up=min(size*(bel[r]+1),n+1);i<up;i++){
if(sub[i]>=0){
maxn[bel[r]]=max(maxn[bel[r]],sub[i]-i);
}else{
maxn[bel[r]]=max(maxn[bel[r]],-sub[i]);
}
}
for(int i=bel[l]+1;i<bel[r];i++){
tag1[i]=x;
if(x>=0){
maxn[i]=x-size*i;
}else{
maxn[i]=-x;
}
}
}
void setpre(int l,int r,int x){
if(tag2[bel[l]]!=-inf){
for(int i=max(size*bel[l],1),up=min(size*(bel[l]+1),n+2);i<up;i++){
pre[i]=tag2[bel[l]];
}
tag2[bel[l]]=-inf;
}
for(int i=l,up=min(r,size*(bel[l]+1)-1);i<=up;i++){
pre[i]=x;
}
if(bel[l]==bel[r]){
return;
}
if(tag2[bel[r]]!=-inf){
for(int i=max(size*bel[r],1),up=min(size*(bel[r]+1),n+2);i<up;i++){
pre[i]=tag2[bel[r]];
}
tag2[bel[r]]=-inf;
}
for(int i=size*bel[r];i<=r;i++){
pre[i]=x;
}
for(int i=bel[l]+1;i<bel[r];i++){
tag2[i]=x;
}
}
int main(){
n=rd(),m=rd();
size=sqrt(n+1);
for(int i=0;i<=n+1;i++){
bel[i]=i/size;
sub[i]=n+1;
pre[i]=0;
maxn[bel[i]]=max(maxn[bel[i]],sub[i]-i);
tag1[bel[i]]=tag2[bel[i]]=-inf;
}
for(int i=1;i<=m;i++){
op=rd();
if(op==1){
p=rd();
ans=0;
for(int i=0;i<=bel[n];i++){
if(maxn[i]>p){
if(tag1[i]!=-inf){
ans=size*i+1;
}else{
for(int j=size*i,up=min(size*(i+1),n+1);j<up;j++){
if(sub[j]>=0&&sub[j]-j>p){
ans=j+1;
break;
}else if(sub[j]<0&&-sub[j]>p){
ans=j+1;
break;
}
}
}
}
if(ans){
break;
}
}
printf("%d
",ans);
if(ans==0){
continue;
}
pr=tag2[bel[ans]]!=-inf?tag2[bel[ans]]:pre[ans];
if(pr<0){
pr=ans+pr;
}
su=tag1[bel[ans+p-1]]!=-inf?tag1[bel[ans+p-1]]:sub[ans+p-1];
if(su<0){
su=ans+p-1-su;
}
setsub(pr,ans-1,ans);
if(p>1){
setsub(ans,ans+p-2,-1);
}
setpre(ans+p,su,ans+p-1);
if(p>1){
setpre(ans+1,ans+p-1,-1);
}
}else{
x=rd(),p=rd();
pr=tag2[bel[x]]!=-inf?tag2[bel[x]]:pre[x];
if(pr<0){
pr=x+pr;
}
su=tag1[bel[x+p-1]]!=-inf?tag1[bel[x+p-1]]:sub[x+p-1];
if(su<0){
su=x+p-1-su;
}
setsub(pr,x+p-1,su);
setpre(x,su,pr);
}
}
return 0;
}