题意:一个旅馆有n个房间,m条操作,如果输入1 b 表示住进来b个人 然后输出这b个人住的房间编号最前面的那个,如果输入 2 b c, 就是说要将b到b+c-1的房间全部全部清空。
思路:此题更新操作简单,但是查询的时候不好做,这题要添加两个数组,lsum表示一个区间从最左端开始可用的且连续的最大长度,例如区间[1,5],覆盖情况为[0,0,0,1,1],lsum=3,从最左端有3格可以利用。区间[1,5],覆盖情况为[1,0,0,0,0],lsum=0,因为从最左端开始找不到1格可用的区间rsum表示到当前节点为止的最长连续的房间个数。rsum表示一个区间从最右端开始可用的且连续的最大长度。例如区间[1,5],覆盖情况为[1,0,1,0,0],rsum=2,从最右端有2格可以利用。区间[1,5],覆盖情况为[0,0,0,0,1],rsum=0,因为从最右端开始找不到可用的区间。而sum存该区间最大的长度。
对于一个区间,我们知道它左半区间的sum,和右半区间的sum,如果左半区间的sum>=W,那么我们一定能在左边找到,所以可以到左半区间去确定该区间的具体位置,如果左端的不满足,那么我们要先考虑横跨两边的区间,一段横跨的区间,那么是左边区间的rsum+右边区间的lsum,如果满足的话,就是该区间了,它的位置也是可以确定的。如果横跨的区间不满足,那么就在右半区间找,如果右半区间的sum>=w, 那么可以在右半区间找到,所以到右半区间去确定它的具体位置。
接下来要说的就是对这三个值进行更新,当前区间的sum=max{左区间的rsum+右区间的lsum,max{左区间的sum,右区间的sum}};
如果左区间全部都可以用,那么当前的lsum=左区间lsum+右区间lsum,不然的话,就表示只有部分房间可以用,那么当前lsum=左区间lsum。
同理如果右区间全部都可以用,当前rsum=右区间rsum+左区间rsum,不然就是当前rsum=右边rsum
#include <stdio.h>
#include <algorithm>
using namespace std;
#define maxn 55555
int lsum[maxn<<2],rsum[maxn<<2],sum[maxn<<2];
int lazy[maxn<<2];
int L,R;
void pushdown(int id,int m)//向下更新。
{
if(lazy[id]!=-1)
{
lazy[id<<1]=lazy[id<<1|1]=lazy[id];
sum[id<<1]=lsum[id<<1]=rsum[id<<1]=lazy[id]?0:m-(m>>1);//如果lazy的值不是0就归0,不然就赋值 m-(m>>1)
sum[id<<1|1]=lsum[id<<1|1]=rsum[id<<1|1]=lazy[id]?0:m>>1;
lazy[id]=-1;
}
}
void pushup(int id,int m)//更新三个数组的值
{
lsum[id]=lsum[id<<1];
rsum[id]=rsum[id<<1|1];
if(lsum[id]==m-(m>>1))
lsum[id]+=lsum[id<<1|1];
if(rsum[id]==m>>1)
rsum[id]+=rsum[id<<1];
sum[id]=max(lsum[id<<1|1]+rsum[id<<1],max(sum[id<<1],sum[id<<1|1]));
}
void build(int l,int r,int id)
{
lazy[id]=-1;
sum[id]=lsum[id]=rsum[id]=r-l+1;
if(l==r)return ;
int mid=l+r>>1;
build(l,mid,id<<1);
build(mid+1,r,id<<1|1);
}
void updata(int c,int l,int r,int id)
{
if(L<=l&&r<=R)
{
sum[id]=lsum[id]=rsum[id]=c?0:r-l+1;
lazy[id]=c;
return ;
}
pushdown(id,r-l+1);
int mid=l+r>>1;
if(L<=mid)updata(c,l,mid,id<<1);
if(R>mid)updata(c,mid+1,r,id<<1|1);
pushup(id,r-l+1);
}
int query(int w,int l,int r,int id)
{
if(l==r)return l;
pushdown(id,r-l+1);//这里也要pushdown ,开始漏掉WA了
int mid=l+r>>1;
if(sum[id<<1]>=w)
return query(w,l,mid,id<<1);
else if(lsum[id<<1|1]+rsum[id<<1]>=w)//横跨区间直接返回,开始用递归的结果错了 QAQ 现在都没有想出来为什么
return mid-rsum[id<<1]+1;
return query(w,mid+1,r,id<<1|1);
}
int main()
{
int m,n,op,a,b;
scanf("%d %d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&a);
if(sum[1]<a)puts("0");
else
{
int p=query(a,1,n,1);
printf("%d\n",p);
L=p;
R=p+a-1;
updata(1,1,n,1);
}
}
else
{
scanf("%d %d",&a,&b);
L=a;
R=a+b-1;
updata(0,1,n,1);
}
}
return 0;
}