题目链接:http://poj.org/problem?id=3667
题意:旅馆的N 个房间初始时全为空。现在有M个操作,分为两种:
(1)1 X,要求得到连续的X个房间,输出X个连续房间的最小编号;如果有多个X的连续房间,输出编号最小的一个;不存在输出0;
(2)2 X Y,将X开始的连续Y个房间退掉。
思路:经典的线段树题目。。。
每个节点保存max,Lmax,Rmax三个值,分别是本区间内最长的连续可用房间数,从左侧开始最长的连续可用房间数,有右侧开始最长的连续可用房间数。这样,对于询问,首先判断区间的max值是否满足,不满足可直接返回0;否则判断Lmax是否满足,满足则返回本区间的L值即可;否则判断左孩子的max值,若满足递归到左孩子;否则,判断左孩子的Rmax加上右孩子的Lmax值是否满足,是则返回左孩子Rmax段的左侧;否则递归到右孩子。对于更新,则在左右孩子返回后修改本区间的max、Lmax和Rmax值。
struct node
{
int L,R,len,mid;
int max,Lmax,Rmax;
};
const int MAX=50005;
node a[MAX<<2];
int n,m;
void build(int t,int L,int R)
{
a[t].L=L;
a[t].R=R;
a[t].mid=(L+R)>>1;
a[t].len=R-L+1;
a[t].Lmax=a[t].Rmax=a[t].max=R-L+1;
if(L==R) return;
build(t*2,L,a[t].mid);
build(t*2+1,a[t].mid+1,R);
}
int query(int t,int x)
{
if(a[t].max<x) return 0;
if(a[t].Lmax>=x) return a[t].L;
if(a[t*2].max>=x) return query(t*2,x);
if(a[t*2].Rmax+a[t*2+1].Lmax>=x) return a[t*2].R-a[t*2].Rmax+1;
return query(t*2+1,x);
}
void update(int t,int L,int R,int st)
{
if(L==a[t].L&&R==a[t].R)
{
a[t].max=a[t].Lmax=a[t].Rmax=st*a[t].len;
return;
}
if(a[t].max==a[t].len||a[t].max==0)
{
a[t*2].max=a[t*2].Lmax=a[t*2].Rmax=a[t*2].len*(a[t].max!=0);
a[t*2+1].max=a[t*2+1].Lmax=a[t*2+1].Rmax=a[t*2+1].len*(a[t].max!=0);
}
if(R<=a[t].mid) update(t*2,L,R,st);
else if(L>a[t].mid) update(t*2+1,L,R,st);
else
{
update(t*2,L,a[t].mid,st);
update(t*2+1,a[t].mid+1,R,st);
}
a[t].Lmax=a[t*2].Lmax;
if(a[t].Lmax==a[t*2].len) a[t].Lmax+=a[t*2+1].Lmax;
a[t].Rmax=a[t*2+1].Rmax;
if(a[t].Rmax==a[t*2+1].len) a[t].Rmax+=a[t*2].Rmax;
a[t].max=max(a[t*2].max,a[t*2+1].max);
a[t].max=max(a[t].max,a[t*2].Rmax+a[t*2+1].Lmax);
}
int main()
{
scanf("%d%d",&n,&m);
build(1,1,n);
int cmd,x,y,t;
while(m--)
{
scanf("%d",&cmd);
if(cmd==1)
{
scanf("%d",&x);
y=query(1,x);
printf("%d\n",y);
if(y) update(1,y,y+x-1,0);
}
else
{
scanf("%d%d",&x,&y);
update(1,x,x+y-1,1);
}
}
return 0;
}