poj3667(bzoj1593)--Usaco08Feb Hotel--线段树区间合并

Posted BK-Edwina

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj3667(bzoj1593)--Usaco08Feb Hotel--线段树区间合并相关的知识,希望对你有一定的参考价值。

Description

奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。 贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到Di (1 <= Di <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r..r+Di -1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。 旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字Xi、Di 描述,表示编号为Xi..Xi+Di -1 (1 <= Xi <= N-Di +1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。 而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M < 50,000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。

Input

* 第1行: 2个用空格隔开的整数:N、M

* 第2..M+1行: 第i+1描述了第i个请求,如果它是一个订房请求,则用2个数字 1、Di描述,数字间用空格隔开;如果它是一个退房请求,用3 个以空格隔开的数字2、Xi、Di描述

Output

* 第1..??行: 对于每个订房请求,输出1个独占1行的数字:如果请求能被满足 ,输出满足条件的最小的r;如果请求无法被满足,输出0

Sample Input

10 6
1 3
1 3
1 3
1 3
2 5 5
1 6 

Sample Output

1
4
7
0
5
 
题解:
  感觉思想上和小白逛公园有一点像。
  此问题抽象化为:
    1.对于一个序列,求是否有连续的一段长为Di的区间的值都为0。如果有,输出最小的左端点编号;如果没有,输出0;
    2.清空一段区间的值。
  这题维护的信息是:
    l_len----从区间左端点开始的最长0子串长度
    r_len----从区间右端点开始的最长0子串长度
    t_len----整个区间内最长0子串长度
    lzy----当前房间状态
  合并都写注释里了。
技术分享
  1 #include<cmath>
  2 #include<algorithm>
  3 #include<cstdio>
  4 #include<iostream>
  5 #include<cstring>
  6 using namespace std;
  7 const int maxn=50009;
  8 
  9 struct tree
 10 {
 11     int lzy,l,r;
 12     int l_len,r_len,t_len;
 13     int get_len()
 14     {
 15         return r-l+1;
 16     }
 17     int get_mid()
 18     {
 19         return (l+r)>>1;
 20     }
 21     void new_len()
 22     {
 23         t_len=r_len=l_len=(lzy?0:get_len());
 24     }
 25 }tr[maxn<<2];
 26 int n,m,d[maxn];
 27 
 28 void build(int x,int la,int ra)
 29 {
 30     tr[x].l=la;tr[x].r=ra;
 31     tr[x].t_len=tr[x].r_len=tr[x].l_len=tr[x].get_len();
 32     tr[x].lzy=0;//初始时旅馆没有客人
 33     if(la==ra)return;
 34     int mid=tr[x].get_mid();
 35     build(x<<1,la,mid);
 36     build(x<<1|1,mid+1,ra);
 37     return;
 38 }
 39 
 40 int ask(int tot,int x)
 41 {
 42     if(tr[x].l==tr[x].r&&tot==1)
 43         return tr[x].l;//只需要一间的时候
 44     if(tr[x].lzy!=-1)
 45     {
 46         tr[x<<1].lzy=tr[x<<1|1].lzy=tr[x].lzy;
 47         tr[x].lzy=-1;
 48         tr[x<<1].new_len();tr[x<<1|1].new_len();
 49     }
 50     if(tr[x<<1].t_len>=tot)//在左区间找
 51         return ask(tot,x<<1);
 52     else if(tr[x<<1].r_len+tr[x<<1|1].l_len>=tot)//中间拼起来的区间刚好覆盖
 53         return tr[x<<1].r-tr[x<<1].r_len+1;//直接返回此区间的左端点
 54     else if(tr[x<<1|1].t_len>=tot)//在右区间里找
 55         return ask(tot,x<<1|1);
 56     else
 57         return 0;//没有空房间啦qwq
 58 }
 59 
 60 void update(int x,int w,int la,int ra)
 61 {
 62     if(tr[x].l==la&&ra==tr[x].r)
 63     {
 64         tr[x].lzy=w;
 65         tr[x].new_len();
 66         return;
 67     }
 68     if(tr[x].lzy!=-1)
 69     {//下传lazy标记
 70         tr[x<<1].lzy=tr[x<<1|1].lzy=tr[x].lzy;
 71         tr[x].lzy=-1;
 72         tr[x<<1].new_len();//更新覆盖情况
 73         tr[x<<1|1].new_len();
 74     }
 75     int mid=tr[x].get_mid();
 76     if(la>mid)
 77         update(x<<1|1,w,la,ra);
 78     else if(ra<=mid)
 79         update(x<<1,w,la,ra);
 80     else
 81     {
 82         update(x<<1,w,la,mid);
 83         update(x<<1|1,w,mid+1,ra);
 84     }
 85     
 86     int tmp=max(tr[x<<1].t_len,tr[x<<1|1].t_len);
 87     tr[x].t_len=max(tmp,tr[x<<1].r_len+tr[x<<1|1].l_len);
 88     tr[x].l_len=tr[x<<1].l_len;
 89     tr[x].r_len=tr[x<<1|1].r_len;
 90     
 91     if(tr[x<<1].t_len==tr[x<<1].get_len())
 92         tr[x].l_len+=tr[x<<1|1].l_len;
 93         
 94     if(tr[x<<1|1].t_len==tr[x<<1|1].get_len())
 95         tr[x].r_len+=tr[x<<1].r_len;
 96     return;
 97 }
 98 
 99 int main()
100 {
101     scanf("%d%d",&n,&m);
102     build(1,1,n);
103     while(m--)
104     {
105         int op;
106         scanf("%d",&op);
107         if(op==1)
108         {
109             int tot;
110             scanf("%d",&tot);
111             int ans=ask(tot,1);
112             cout<<ans<<endl;//从头开始找
113             if(ans)
114                 update(1,1,ans,ans+tot-1);//标记已经住进去
115         }
116         else if(op==2)
117         {
118             int la,len;
119             scanf("%d%d",&la,&len);
120             update(1,0,la,la+len-1);//清空房间
121         }
122     }
123     return 0;
124 }
View Code

 

以上是关于poj3667(bzoj1593)--Usaco08Feb Hotel--线段树区间合并的主要内容,如果未能解决你的问题,请参考以下文章

[POJ3667]Hotel

Hotel POJ - 3667 (区间合并 + 区间查询)

POJ 3667 Hotel

POJ 3667 Hotel

poj 3667 Hotel - 线段树

[POJ3667]Hotel