题目链接:Hotel POJ - 3667
题意:安排住房,n个连续的房间编号为1~n,m次操作,一种是安排连续的k个房间,一种是从房间i开始,退房k个。
如果我们将没有人住的房间标为1,有人住的标为0,那么就可以将这个问题转化为求区间最大子段和。
于是延迟标记可以设置为两种,退房时将标记设为1,住房时设为0。
因为安排房间的是你,所以为了不必要的麻烦,我们尽量将房间开的靠前一点。想一想如果随便开,比如有10个房间,先开8个占了2~8,如果再开两个的话就开不了了。
维护四个东西,区间和,区间紧靠左端点的最大值,紧靠右端点的最大值,区间最大子段和。
维护这四个东西可以参考:区间最大子段和
与区间最大子段和稍有不同的是合并两个区间的时候,维护紧靠区间左端点与右端点的最大值与之有所不同,请看代码。
查询的时候注意一下,类似于求第k大的思想,假设要安排x个连续房间,
- 如果左子区间的最大子段和>=x,那么去左子树查
- 如果两段子区间合并>=x,那么就可以返回答案了
- 如果右子区间的最大子段和>=x,那么就查右子树
- 如果都不行,则说明开不了房间。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+7;
struct Node{
int sum;
int lmax;
int rmax;
int dat;
}a[maxn<<2|1];
int lazy[maxn<<2|1];
void pushup(int l,int r,int k){
int mid=(l+r)>>1;
a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
a[k].lmax=a[k<<1].lmax;
//如果要取右子树,为了保证紧靠左端点,左子树必须取所有元素才行,为了保证房间连续,我们必须加入判断房子个数语句;
if(a[k<<1].sum==mid-l+1) a[k].lmax=max(a[k].lmax,a[k<<1].sum+a[k<<1|1].lmax);
a[k].rmax=a[k<<1|1].rmax;
if(a[k<<1|1].sum==r-mid) a[k].rmax=max(a[k].rmax,a[k<<1|1].sum+a[k<<1].rmax);
a[k].dat=max(max(a[k<<1].dat,a[k<<1|1].dat),a[k<<1].rmax+a[k<<1|1].lmax);
}
void pushdown(int l,int r,int k){
int mid=(l+r)>>1;
if(lazy[k]!=-1){
lazy[k<<1]=lazy[k<<1|1]=lazy[k];
if(lazy[k]==1){
a[k<<1].sum=a[k<<1].lmax=a[k<<1].rmax=a[k<<1].dat=(mid-l+1);
a[k<<1|1].sum=a[k<<1|1].lmax=a[k<<1|1].rmax=a[k<<1|1].dat=r-mid;
}
else{
a[k<<1].sum=a[k<<1].lmax=a[k<<1].rmax=a[k<<1].dat=a[k<<1|1].sum=a[k<<1|1].lmax=a[k<<1|1].rmax=a[k<<1|1].dat=lazy[k];
}
lazy[k]=-1;
}
}
void build(int l,int r,int k){
lazy[k]=-1;
if(l==r){
a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
pushup(l,r,k);
}
void updata(int l,int r,int k,int L,int R,int val){
if(l>=L&&r<=R){
if(val==1){
a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=r-l+1;
}
else{
a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=val;
}
lazy[k]=val;
return ;
}
pushdown(l,r,k);
int mid=(l+r)>>1;
if(L<=mid) updata(l,mid,k<<1,L,R,val);
if(R>mid) updata(mid+1,r,k<<1|1,L,R,val);
pushup(l,r,k);
}
int n;
int myfind(int l,int r,int k,int x){
if(l==r){
if(a[k].sum==1&&x==1) return l;
return -1;
}
pushdown(l,r,k);
//if(a[k].dat<x) return -1;
int mid=(l+r)>>1;
int lz=a[k<<1].dat,rz=a[k<<1|1].dat;
if(lz>=x) return myfind(l,mid,k<<1,x);
else if(a[k<<1].rmax+a[k<<1|1].lmax>=x) return mid-a[k<<1].rmax+1;
else if(rz>=x) return myfind(mid+1,r,k<<1|1,x);
return -1;
}
int main(){
int m;
scanf("%d%d",&n,&m);
build(1,n,1);
int id,l,r;
while(m--){
scanf("%d%d",&id,&l);
if(id==1){
int res=myfind(1,n,1,l);
if(res==-1) printf("0\n");
else{
printf("%d\n",res);
updata(1,n,1,res,res+l-1,0);
}
}
else{
scanf("%d",&r);
updata(1,n,1,l,l+r-1,1);
}
}
}