【POJ3667】Hotel-线段树

(本人本题完成于2016-7-22)

题目大意:有一个旅馆有N个房间,标号为1,2,...,n,刚开始它们都是空着的。有M个操作,分为两种:1.找到标号最小的连续的D个房间并入住,并输出这些房间中标号最小的房间的标号,如果找不到这样的连续房间则输出0。2.清空从X号房间开始的D个房间。

做法:建一棵线段树,除区间左右端点外维护:sum:该区间内的最大连续空区间长度。lsum:该区间的最左边的连续空区间长度。rsum:该区间的最右边的连续空区间长度。p:标记该区间是否全空(p=1)或全满(p=0),p=-1时表示没有标记。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,o,a,x;
struct node
{
  int l,r;
  int lsum,rsum,sum,p;
}seg[200010];

void buildtree(int no,int l,int r) //建立线段树
{
  int mid=(l+r)/2;
  seg[no].l=l;seg[no].r=r;
  seg[no].sum=seg[no].lsum=seg[no].rsum=r-l+1;
  seg[no].p=-1;
  if (l!=r)
  {
	buildtree(2*no,l,mid);
	buildtree(2*no+1,mid+1,r);
  }
}

void pushdown(int no) //将节点no的标记下放
{
  if (seg[no].p!=-1)
  {
    seg[2*no].p=seg[2*no+1].p=seg[no].p;
    if (!seg[2*no].p)
	{
	  seg[2*no].lsum=seg[2*no].rsum=seg[2*no].sum=0;
	  seg[2*no+1].lsum=seg[2*no+1].rsum=seg[2*no+1].sum=0;
	}
	else
	{
	  seg[2*no].lsum=seg[2*no].rsum=seg[2*no].sum=seg[2*no].r-seg[2*no].l+1;
	  seg[2*no+1].lsum=seg[2*no+1].rsum=seg[2*no+1].sum=seg[2*no+1].r-seg[2*no+1].l+1;
	}
	seg[no].p=-1;
  }
}

void pushup(int no) //用更新后的no的儿子的数值来更新节点no
{
  seg[no].lsum=seg[2*no].lsum;
  seg[no].rsum=seg[2*no+1].rsum;
  if (seg[2*no].lsum==seg[2*no].r-seg[2*no].l+1) seg[no].lsum+=seg[2*no+1].lsum;
  if (seg[2*no+1].rsum==seg[2*no+1].r-seg[2*no+1].l+1) seg[no].rsum+=seg[2*no].rsum;
  seg[no].sum=max(max(seg[2*no].sum,seg[2*no+1].sum),seg[2*no].rsum+seg[2*no+1].lsum);
}

int query(int no,int a) //查询并返回标号最小的长度为a的连续空区间的左端点标号
{
  int mid=(seg[no].l+seg[no].r)/2;
  if (seg[no].l==seg[no].r) return seg[no].l;
  pushdown(no);
  if (seg[2*no].sum>=a) return query(2*no,a);
  if (seg[2*no].rsum+seg[2*no+1].lsum>=a) return mid-seg[2*no].rsum+1;
  if (seg[2*no+1].sum>=a) return query(2*no+1,a);
  return -1;
}

void checkin(int no,int s,int t) //将区间(s,t)覆盖
{
  int mid=(seg[no].l+seg[no].r)/2;
  if (seg[no].l>=s&&seg[no].r<=t)
  {
    seg[no].lsum=seg[no].rsum=seg[no].sum=0;
	seg[no].p=0;
	return;
  }
  pushdown(no);
  if (s<=mid) checkin(2*no,s,t);
  if (t>mid) checkin(2*no+1,s,t);
  pushup(no);
}

void checkout(int no,int s,int t) //清空区间(s,t)
{
  int mid=(seg[no].l+seg[no].r)/2;
  if (seg[no].l>=s&&seg[no].r<=t)
  {
    seg[no].lsum=seg[no].rsum=seg[no].sum=seg[no].r-seg[no].l+1;
	seg[no].p=1;
    return;
  }
  pushdown(no);
  if (s<=mid) checkout(2*no,s,t);
  if (t>mid) checkout(2*no+1,s,t);
  pushup(no);
}

int main()
{
  scanf("%d %d",&n,&m);
  buildtree(1,1,n);
  for(int i=1;i<=m;i++)
  {
    scanf("%d",&o);
	if (o==1)
	{
	  scanf("%d",&a);
	  if (seg[1].sum<a) printf("0\n"); //如果没有那么长的连续空区间,输出0
	  else
	  {
	    int f=query(1,a);
		printf("%d\n",f);
		checkin(1,f,f+a-1);
	  }
	}
	if (o==2)
	{
	  scanf("%d %d",&a,&x);
	  checkout(1,a,a+x-1);
	}
  }
  
  return 0;
}


posted @ 2016-07-22 22:11  Maxwei_wzj  阅读(76)  评论(0编辑  收藏  举报