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 }

时间: 2024-08-29 08:24:34

poj3667(bzoj1593)--Usaco08Feb Hotel--线段树区间合并的相关文章

POJ 3667 Hotel(线段树区间合并)

Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Stree

POJ 3667 Hotel ( 线段树区间合并 )

题目链接~~> 做题感悟:这题是接触线段树区间合并的第一题,做的很纠结. 解题思路: 注意线段树上节点代表的信息 : 每个节点需要维护 lc , rc , mc ,add ,见下图: add 为懒惰标记.假设 i 代表父亲节点编号,左儿子为  i * 2  ,右儿子为 i * 2  + 1 ,那么我们可以得到 : T [ i ] .lc 首先加上左儿子的左边的空格数,然后需要判断一下,如果左儿子的左节点占满了整个左区间时需要再加上右儿子的左边的空格数.同理 T [ i ] .rc 也可以这样得到

POJ 3667 Hotel 线段树 区间合并

题意: 1 输入a:询问是不是有连续长度为a的空房间,有的话住进最左边 2 输入a b:将[a,a+b-1]的房间清空 思路:记录区间中最长的空房间 线段树操作: update:区间替换 query:询问满足条件的最左端点 #include <cstdio> #include <iostream> #include <algorithm> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1

POJ 3667 Hotel 【线段树 区间合并 + Lazy-tag】

Hotel Time Limit: 3000MS Memory Limit: 65536K 链接:POJ 3667   Description The cows are journeying north to ThunderBay in Canada to gain cultural enrichment and enjoy a vacation on the sunnyshores of Lake Superior. Bessie, ever the competent travel agen

Poj 3667——hotel——————【线段树区间合并】

Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 13124   Accepted: 5664 Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie

线段树(区间合并) POJ 3667 Hotel

题目传送门 1 /* 2 题意:输入 1 a:询问是不是有连续长度为a的空房间,有的话住进最左边 3 输入 2 a b:将[a,a+b-1]的房间清空 4 线段树(区间合并):lsum[]统计从左端点起最长连续空房间数,rsum[]类似,sum[]统计区间最长连续的空房间数, 5 它有三种情况:1.纯粹是左端点起的房间数:2.纯粹是右端点的房间数:3.当从左(右)房间起都连续时,加上另一个子节点 6 从左(右)房间起的数,sum[]再求最大值更新维护.理解没错,表达能力不足 7 详细解释:htt

POJ 3667 Hotel (线段树区间合并 )

Language: Default Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12417   Accepted: 5346 Description The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lak

poj3667 线段树 区间合并

1 //Accepted 3728 KB 1079 ms 2 //线段树 区间合并 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 #include <queue> 7 #include <cmath> 8 #include <algorithm> 9 using namespace std; 10 /** 11 * This is a document

POJ 3667 Hotel (初遇线段树区间合并)

题意: 有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作 1 w 表示在总区间内查询一个长度为w的可用区间并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出. 思路: 这是第一次遇到线段树区间合并的题目,写下感悟,还是对线段的更新和查询工作,但是查询的对象的性质已经不像单点那样,查询的是某个线段的最大可用区间是多少,还要一并