第一行输入n,m ,n代表有n个房间,编号为1---n,开始都为空房,m表示以下有m行操作,以下 每行先输入一个数 i ,表示一种操作:
若i为1,表示查询房间,再输入一个数x,表示在1--n 房间中找到长度为x的连续空房,输出连续x个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为x的连续空房,输出0,并且在这x个空房间中住上人。
若i为2,表示退房,再输入两个数 x,y 代表 房间号 x---x+y-1 退房,即让房间为空。
Solution
显然用线段树维护,用类似最大子段和的方法处理
考虑 update
,无非就是更新单侧值,然后再讨论三种情况更新一下 ans
比较揪心的是如何查询,我们考虑如何在查询时讨论
如果到达递归边界就表明找到,返回
如果左区间足够多就去左区间
否则,如果中间足够多,就直接返回左区间从右开始的最长连续的左端点
否则去右区间找
#include
using namespace std;
const int N = 2000005;
int ml[N],mr[N],mx[N],len[N],tag[N];
void build(int p,int l,int r) {
ml[p]=mr[p]=mx[p]=len[p]=r-l+1;
if(lqr || r=ql&&r<=qr) {
if(c) ml[p]=mr[p]=mx[p]=len[p], tag[p]=1;
else ml[p]=mr[p]=mx[p]=0, tag[p]=-1;
}
else {
pushdown(p);
modify(p*2,l,(l+r)/2,ql,qr,c);
modify(p*2+1,(l+r)/2+1,r,ql,qr,c);
update(p);
}
}
int query(int p,int l,int r,int ql,int qr,int k) {
if(l==r) return l;
pushdown(p);
if(mx[p*2]>=k) return query(p*2,l,(l+r)/2,ql,qr,k);
if(mr[p*2]+ml[p*2+1]>=k) return (l+r)/2-mr[p*2]+1;
return query(p*2+1,(l+r)/2+1,r,ql,qr,k);
}
int n,m,t1,t2,t3;
int main() {
scanf("%d%d",&n,&m);
build(1,1,n);
for(int i=1;i<=m;i++) {
scanf("%d",&t1);
if(t1==1) {
scanf("%d",&t2);
if(mx[1]>=t2) {
t3=query(1,1,n,1,n,t2);
printf("%d\n",t3);
modify(1,1,n,t3,t3+t2-1,0);
}
else printf("0\n");
}
else {
scanf("%d%d",&t2,&t3);
modify(1,1,n,t2,t2+t3-1,1);
}
}
}