区间连续长度的线段树——洛谷P2894 [USACO08FEB]酒店Hotel

https://www.luogu.org/problem/P2894

#include<cstdio>
#include<iostream>
using namespace std;
struct ben
{
	int lmax,rmax,len,sum,lz;
}tr[400005];
void bt(int x,int l,int r)//建树 
{
	tr[x].lz=0;//标记清空 
	tr[x].sum=tr[x].len=tr[x].lmax=tr[x].rmax=r-l+1;//区间连续,区间长度,左端点连续,右端点连续都初始化成区间的长度。(因为所有房间都是空的 
	if(l==r)//如果到了叶子节点 
	{
		return ;//返回 
	}
	int mid=(l+r)/2;
	bt(x*2,l,mid);
	bt(x*2+1,mid+1,r);
	//左右建子节点 
}
void down(int x)//标记下放 
{
	if(tr[x].lz==0)return ;//如果没有标记,直接返回 
	if(tr[x].lz==1)//如果是要开房 
	{
		tr[x*2].lz=tr[x*2+1].lz=1;//左右子节点都要标记上开房 
		tr[x*2].rmax=tr[x*2].lmax=tr[x*2].sum=0;
		tr[x*2+1].rmax=tr[x*2+1].lmax=tr[x*2+1].sum=0; 
		//左右子节点因为已经开房而没有空房都变成0 
	}
	if(tr[x].lz==2)//如果要退房 
	{
		tr[x*2].lz=tr[x*2+1].lz=2;////左右子节点都要标记上退房 
		tr[x*2].rmax=tr[x*2].lmax=tr[x*2].sum=tr[x*2].len;
		tr[x*2+1].rmax=tr[x*2+1].lmax=tr[x*2+1].sum=tr[x*2+1].len; 
		//此区间已经全为空房,所有直接都等于len即可 
	}
	tr[x].lz=0;//要把父节点的标记清空 
}
void renew(int x)//更新节点信息 
{
	if(tr[x*2].sum==tr[x*2].len)//如果左子节点全为空房 
	{
		tr[x].lmax=tr[x*2].sum+tr[x*2+1].lmax;//那么父节点的左端最大连续为左子区间加上右子区间从左的最大连续 
	}
	else
	{
		tr[x].lmax=tr[x*2].lmax;//否则就是沿用左从左 
	}
	if(tr[x*2+1].sum==tr[x*2+1].len)//右边同理 
	{
		tr[x].rmax=tr[x*2+1].sum+tr[x*2].rmax;
	
	}
	else 
	{
		tr[x].rmax=tr[x*2+1].rmax;
	}
	tr[x].sum=max(max(tr[x*2].sum,tr[x*2+1].sum),tr[x*2].rmax+tr[x*2+1].lmax);//最大连续的是左区间或右区间或左+右中最大的 
}
void change_interval(int x,int l,int r,int tag,int L,int R)//区间修改,tag=1表示开房,tag=2表示退房 
{
	down(x);//下放懒标记 
	if(L<=l&&r<=R)//当前要查`询的区间如果在区间内 
	{
		if(tag==1)
		{
			tr[x].sum=tr[x].lmax=tr[x].rmax=0;//开房就没有连续 
		}
		else
		{
			tr[x].sum=tr[x].lmax=tr[x].rmax=tr[x].len;//退房就全是连续 
		}
		tr[x].lz=tag;//打个懒标记 
		return ; //别忘返回,因为在整个查询区间内 
	}
	int mid=(l+r)/2;
	if(L<=mid)change_interval(x*2,l,mid,tag,L,R);
	if(R>mid)change_interval(x*2+1,mid+1,r,tag,L,R); 
	renew(x);//更新当前节点 信息 
}
int ask(int x,int l,int r,int ans)//查询有没有那么多连续的空房间 
{
	down(x);//下放懒标记 
	if(l==r)return l;//到叶子了,直接反回 
	int mid=(l+r)/2;
	if(tr[x*2].sum>=ans)return ask(x*2,l,mid,ans);//如果左边就够用了 ,那么去左边找 (不要忘了是return !!!!!!! 
	if(tr[x*2].rmax+tr[x*2+1].lmax>=ans)return mid-tr[x*2].rmax+1;//否则左+右够了,那么就找左加右那个端点 
	return ask(x*2+1,mid+1,r,ans);//再否则就只能在右区间找了 
}
int main()
{
	int n,m,opt,x,y;
	scanf("%d%d",&n,&m);
	bt(1,1,n); 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&opt,&x);
		if(opt==1)
		{
			if(tr[1].sum>=x)
			{
				int left=ask(1,1,n,x);
				printf("%d\n",left);
				change_interval(1,1,n,1,left,left+x-1);
			}
			else
			{
				printf("0\n");
			}
		}
		else
		{
			scanf("%d",&y);
			change_interval(1,1,n,2,x,x+y-1);
		}
	}
	return 0;
} 
posted @ 2019-08-18 11:53  ShineEternal  阅读(173)  评论(0编辑  收藏  举报