题目链接:POJ-3667
主要思路:
为了书写方便,记Llen为该区间从其左端点向右开始的最长合法区间,Rlen为从右端点向左开始的最长合法区间.
此题用线段树写应该是很好看出来的(从那么多的区间操作中).维护每个区间的Llen与Rlen,还有一个区间中最长的合法区间(方便1操作判断是否有位置).查找时从最大的区间[1,n]开始,先判断这个区间Llen是否满足,再判断这个区间的左子区间的最长合法区间是否满足,再判断这个区间的Llen的Rlen加上Rlen的Llen是否满足(可能有些绕口,要耐心理解),若前三个都不满足则只有可能在Rlen中.(注意删除时要用到懒惰记号).
维护的过程在两个区间合并时体现,一个区间的Llen为其左子区间的Llen,若其左子区间的Llen长度等于其左子区间的长度.其Rlen同理.其中最长合法区间为其左子区间的最长合法区间与其右子区间的最长合法区间还有其左子区间的Rlen+其右区间的Llen三者中的最大值.
AC代码:
#include<cstdio>
#define M 50005
int max(int x,int y){return x>y?x:y;}
struct node{
int L,R,Len,Llen,Rlen,add;//add为懒惰标记 Len为其最长合法区间
void clear(){//此区间清0 要设置懒惰标记使其子区间也清0
Llen=Rlen=Len=0;
add=-1;
}
void Init(){//此区间全部重置(即房间全部空余)
Llen=Rlen=Len=R-L+1;
add=1;
return;
}
int size(){
return R-L+1;
}
};
struct Segment{
node tree[M<<2];
void Up(int p){
tree[p].Len=max(tree[p<<1].Len,tree[p<<1|1].Len);
tree[p].Llen=tree[p<<1].Llen;
tree[p].Rlen=tree[p<<1|1].Rlen;
if(tree[p<<1].Llen==tree[p<<1].size())tree[p].Llen+=tree[p<<1|1].Llen;
if(tree[p<<1|1].Rlen==tree[p<<1|1].size())tree[p].Rlen+=tree[p<<1].Rlen;
tree[p].Len=max(tree[p].Len,tree[p<<1].Rlen+tree[p<<1|1].Llen);
}
void Down(int p){
if(tree[p].add==0)return;
if(tree[p].add==1){//判断懒惰标记
tree[p<<1].Init();
tree[p<<1|1].Init();
}else tree[p<<1].clear(),tree[p<<1|1].clear();
tree[p].add=0;
}
void Build(int L,int R,int p){
tree[p].L=L,tree[p].R=R;
tree[p].Init();
tree[p].add=0;//这样可以省时间
if(L==R)return;
int mid=L+R>>1;
Build(L,mid,p<<1);
Build(mid+1,R,p<<1|1);
}
void Come(int L,int R,int p){
if(tree[p].L==L&&tree[p].R==R){
tree[p].clear();
return;
}
Down(p);//向子区间传递信息
int mid=tree[p].L+tree[p].R>>1;
if(R<=mid)Come(L,R,p<<1);
else if(L>mid)Come(L,R,p<<1|1);
else Come(L,mid,p<<1),Come(mid+1,R,p<<1|1);
Up(p);//从子区间收集信息
}
void Back(int L,int R,int p){
if(tree[p].L==L&&tree[p].R==R){
tree[p].Init();
return;
}
Down(p);
int mid=tree[p].L+tree[p].R>>1;
if(R<=mid)Back(L,R,p<<1);
else if(L>mid)Back(L,R,p<<1|1);
else Back(L,mid,p<<1),Back(mid+1,R,p<<1|1);
Up(p);
}
int Find(int d,int p){
if(tree[p].Llen>=d)return tree[p].L;//看其区间内的包含其左端点的最长合法区间
Down(p);//注意此时要向子区间传递信息
if(tree[p<<1].Len>=d)return Find(d,p<<1);//找其左子区间
if(tree[p<<1].Rlen+tree[p<<1|1].Llen>=d)return tree[p<<1].R-tree[p<<1].Rlen+1;//判断左子区间的Rlen+其右子区间的Llen
return Find(d,p<<1|1);//若都不行则可行解一定在他右子区间内
}
}S;
int main(){
int n,m;
scanf("%d%d",&n,&m);
S.Build(1,n,1);
while(m--){
int a,b,flag;
scanf("%d",&flag);
if(flag==1){
scanf("%d",&a);
if(S.tree[1].Len>=a){
int ans=S.Find(a,1);
printf("%d\n",ans);
S.Come(ans,ans+a-1,1);
}
else puts("0");
}else{
scanf("%d%d",&a,&b);
S.Back(a,a+b-1,1);
}
}
}