题目链接 poj3667 Hotel (此题代码有详细注释,作为初学者可以参考代码理解)
题目大意:让你对一个区间进行操作。输入Q C 或者 Q C D。
Q ==1 输入 C: 表示让你求1-n中是否有连续的C个空hotel,如果有多个连续的C个空hotel,则取最左边的,并输出最左边的第一间hotel标号。让人住进去,那么这些空房就不能住人了。如果不存在连续的C个hotel,则输出 0。
Q==2 输入 C D: 表示 从标号C到C+D-1的hotel的房间全部要退房,那么这么hotel都变成空。
解题思路:记录整段区间最长的空房间。
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define lz 2*u,l,mid
#define rz 2*u+1,mid+1,r
const int maxn=50005;
int flag[4*maxn]; ///标记
struct node
{
int lm; ///从左边第一个点开始最长的连续空hotel
int rm; ///以右边最后一个结束的最长的连续空hotel
int sm; ///整段区间最大的连续空hotel
} tree[4*maxn];
void push_up(int u, int l, int r) ///向上更新
{
tree[u].lm=tree[2*u].lm;
tree[u].rm=tree[2*u+1].rm;
tree[u].sm=max(tree[2*u].sm,tree[2*u+1].sm);
int mid=(l+r)>>1;
if(tree[2*u].lm==mid-l+1) tree[u].lm+=tree[2*u+1].lm; ///!!这里注意,当左孩子左边连续的达到整个区间时,要加上右孩子的左边区间
if(tree[2*u+1].rm==r-mid) tree[u].rm+=tree[2*u].rm; ///!!考虑右区间,同上
int t=tree[2*u].rm+tree[2*u+1].lm;
if(t>tree[u].sm) tree[u].sm=t;
}
void push_down(int u, int l, int r) ///向下更新
{
if(flag[u]==-1) return ;
if(flag[u])
{
flag[2*u]=flag[2*u+1]=flag[u];
tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=0;
tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=0;
flag[u]=-1;
}
else
{
flag[2*u]=flag[2*u+1]=flag[u];
int mid=(l+r)>>1;
tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=mid-l+1;
tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=r-mid;
flag[u]=-1;
}
}
void build(int u, int l, int r) ///建树
{
flag[u]=-1;
if(l==r)
{
tree[u].lm=tree[u].rm=tree[u].sm=1;
return ;
}
int mid=(l+r)>>1;
build(lz);
build(rz);
push_up(u,l,r);
}
void Update(int u, int l, int r, int tl, int tr, int c) ///更新操作
{
if(tl<=l&&r<=tr)
{
tree[u].sm=tree[u].lm=tree[u].rm=(c==1?0:r-l+1);
flag[u]= c;
return ;
}
push_down(u,l,r); ///再次遇见此段区间时,延迟标记同步向下更新
int mid=(l+r)>>1;
if(tr<=mid) Update(lz,tl,tr,c);
else if(tl>mid) Update(rz,tl,tr,c);
else
{
Update(lz,tl,mid,c); ///注意区间分隔开,tl,tr跨越两个左右区间
Update(rz,mid+1,tr,c);
}
push_up(u,l,r); ///递归的时候同步向上更新
}
int Query(int u, int l, int r, int num) ///询问操作
{
if(l==r)
return l;
push_down(u,l,r); ///延迟标记向下传递
int mid=(l+r)>>1;
if(tree[2*u].sm>=num) return Query(lz,num);
else if(tree[2*u].rm+tree[2*u+1].lm>=num&&tree[2*u].rm>=1) return mid-tree[2*u].rm+1; ///满足条件时,返回左边rm连续的hotel第一个房间标号
else
return Query(rz,num);
}
int main()
{
int n, m;
while(~scanf("%d%d",&n,&m))
{
build(1,1,n);
while(m--)
{
int p, u, v;
scanf("%d",&p);
if(p==1)
{
scanf("%d",&u);
if(tree[1].sm<u) ///特判一下是否有这么多个连续的空hotel,没有则直接输出,不用操作
{
puts("0"); continue;
}
int p=Query(1,1,n,u);
printf("%d\n",p);
Update(1,1,n,p,p+u-1,1);
}
else
{
scanf("%d%d",&u,&v);
Update(1,1,n,u,u+v-1,0);
}
}
}
return 0;
}