题意:
给出n个房间,m个操作,操作有两种
操作1:需要x个连续的房间,如果存在则输出开始的房间,并占用,要求尽量小,没有的话则输出0.
操作2:从x开始的连续y个房间,解除占用。
关于区间合并:
区间合并问题一般是要求满足条件的一段连续的区间,个人觉得这类问题一个精华所在就是线段树的pushup函数的写法,如下:
/*ls表示区间的满足条件最长连续前缀,rs表示最长连续后缀,
ms表示该区间的最长连续序列。*/
void pushup(int l,int r,int rt)
{
int m=(r+l)>>1;
ls[rt]=ls[rt<<1];
if(ls[rt<<1]==m-l+1) ls[rt]+=ls[rt<<1|1];
rs[rt]=rs[rt<<1|1];
if(rs[rt<<1|1]==r-m) rs[rt]+=rs[rt<<1];
ms[rt]=max(max(ms[rt<<1],ms[rt<<1|1]),rs[rt<<1]+ls[rt<<1|1]);
//在左边区间还是右边区间或者是跨区间
}
在这类问题种我们需要维护三个值,ls、rs、ms数组。我们要求的是区间内的最长连续序列,跟它的前缀和后缀又有什么关系呢?这就涉及到区间合并了。
一段区间的最长连续序列有三种情况:
第一种:最长连续序列起点和终点都在左区间,也就是只跟左区间有关。ms[rt<<1] 是一个候选人。
第二种:都在右区间。ms[rt<<1|1]是一个候选人。
第三种:起点在左区间,终点在右区间,那么这个区间长度就是左区间后缀加上右区间前缀,就用到了前缀和后缀。
对于每一个结点,都按照这样的方式去维护三个值,这样的方法就是解决这类问题的基本方法了。
思路:
本题其实就是求区间最长连续0序列。
修改操作是比较好实现的,解除占用只要将区间的ls、rs、ms修改为本区间的长度即可,因为区间全部变为了0,以上三个值都是区间长度。
接下来就是询问操作,首先将区间的最长连续序列与所需要的连续房间相比较,如果ms[1],就是整个区间的最长连续序列都不能满足,直接输出0.如果可以那么一定有区间可以满足。
依然是三种情况,因为要求房间最小,从左到右判断。
先是左区间满不满足,满足则深入左区间,答案就在左区间。
否则就是跨区间,如果满足可以直接返回第一个房间的位置。
然后就是右区间。
递归的结束是跨区间,因为每一个连续的区间都可以分成跨区间或者就是l==r这个情况是开一个房间,这里讲的有点迷,写的时候讨论所有情况这也就自然写出来了。
代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const int maxn=5e4+10;
int ls[maxn<<2],rs[maxn<<2],ms[maxn<<2],col[maxn<<2];
int n,m;
/*区间合并正常pushup*/
void pushup(int l,int r,int rt)
{
int m=(r+l)>>1;
ls[rt]=ls[rt<<1];
if(ls[rt<<1]==m-l+1) ls[rt]+=ls[rt<<1|1];
rs[rt]=rs[rt<<1|1];
if(rs[rt<<1|1]==r-m) rs[rt]+=rs[rt<<1];
ms[rt]=max(max(ms[rt<<1],ms[rt<<1|1]),rs[rt<<1]+ls[rt<<1|1]);
}
void pushdown(int l,int r,int rt)
{
int m=(r+l)>>1;
if(col[rt]!=-1)
{
/*分0和1两种情况*/
col[rt<<1]=col[rt<<1|1]=col[rt];
if(col[rt]==0)
{
ls[rt<<1]=rs[rt<<1]=ms[rt<<1]=0;
ls[rt<<1|1]=rs[rt<<1|1]=ms[rt<<1|1]=0;
}
else if(col[rt]==1)
{
ls[rt<<1]=rs[rt<<1]=ms[rt<<1]=m-l+1;
ls[rt<<1|1]=rs[rt<<1|1]=ms[rt<<1|1]=r-m;
}
col[rt]=-1;//记得修改为-1
}
}
void build(int l,int r,int rt)
{
col[rt]=-1;
int m=(r+l)>>1;
if(l==r)
{
ls[rt]=rs[rt]=ms[rt]=1;
return;
}
build(lson);
build(rson);
pushup(l,r,rt);//向上更新
return;
}
/*正常区间修改*/
void updata(int ql,int qr,int c,int l,int r,int rt)
{
int m=(r+l)>>1;
if(ql<=l&&r<=qr)
{
ls[rt]=rs[rt]=ms[rt]=c*(r-l+1);//乘上区间长度
col[rt]=c;
return;
}
pushdown(l,r,rt);
if(ql<=m) updata(ql,qr,c,lson);
if(m<qr) updata(ql,qr,c,rson);
pushup(l,r,rt);
}
/*因为要找最左边的,所以先看左区间的最大值是不是符合如果符合就深入左区间。
如果不符合就看跨区间的情况。
再不行就深入右区间。
如果w=1的话会搜索到l==r。递归结束条件l==r或者跨区间*/
int query(int w,int l,int r,int rt)
{
int m=(r+l)>>1;
if(l==r) return l;
pushdown(l,r,rt);
if(ms[rt<<1]>=w) return query(w,lson);//,除了1个的情况,总会转到跨区间的情况
else if(rs[rt<<1]+ls[rt<<1|1]>=w) return m-rs[rt<<1]+1;
return query(w,rson);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++)
{
int op;
scanf("%d",&op);
if(op==1)
{
int d;
scanf("%d",&d);
//printf("ms[1]:%d\n",ms[1]);
if(ms[1]<d) printf("0\n");
else
{
int p=query(d,1,n,1);
printf("%d\n",p);
updata(p,p+d-1,0,1,n,1);
}
}
else if(op==2)
{
int x,d;
scanf("%d%d",&x,&d);
updata(x,x+d-1,1,1,n,1);
//printf("ms[1]:%d\n",ms[1]);
}
}
return 0;
}