poj 3667 Hotel (线段树的合并操作)

Hotel

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 Street as their vacation residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located on the same side of an extremely long hallway (all the better to see the lake, of course).

The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N) and approach the front desk to check in. Each group i requests a set of Di contiguous rooms from Canmuu, the moose staffing the counter. He assigns them some set of consecutive room numbers r..r+Di-1 if they are available or, if no contiguous set of rooms is available, politely suggests alternate lodging. Canmuu always chooses the value of rto be the smallest possible.

Visitors also depart the hotel from groups of contiguous rooms. Checkout i has the parameters Xi and Di which specify the vacating of rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms might be empty before the checkout.

Your job is to assist Canmuu by processing M (1 ≤ M < 50,000) checkin/checkout requests. The hotel is initially unoccupied.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a check-in request: 1 and D(b) Three space-separated integers representing a check-out: 2, Xi, and Di


Output

* Lines 1.....: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms to be occupied. If the request cannot be satisfied, output 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到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作

1 w  表示在总区间内查询一个长度为w的可用区间,并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0

好像n=10 , 1 3 查到的最左的长度为3的可用区间就是[1,3],返回1,并且该区间被占用了

2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出

因此看sample的话就可以理解了

记录一下自己的感悟:

用线段树,首先要定义好线段树的节点信息,一般看到一个问题,很难很快能确定线段树要记录的信息
做线段树不能为了做题而做,首先线段树是一种辅助结构,它是为问题而生的,因而必须具体问题具体分析
回忆一下RMQ问题,其实解决RMQ有很多方法,根本不需要用到线段树,用线段树解决RMQ,其实是利用线段树的性质来辅助解决这个问题
回忆一下求矩形面积并或周长并的问题,一般使用的是扫描线法,其实扫描线法和线段树一点关系都没有,扫描线法应该归为计算几何的算法,
使用线段树只是为了辅助实现扫描线法

因而回到这题,要解,必须分析问题本质,才去思考怎么用线段树来辅助,另外为什么能用线段树辅助是可行的,这个问题似乎更有价值

1 查询操作,找一段长度为W的没被覆盖的最左的区间
2 更新操作,将某段连续的区域清空

更新操作相对容易解决,关键是怎么实现查询操作
既然是要找一段长度至少为W的区间,要做到这点,其实不难,我们可以在每个线段树的节点里增加一个域tlen,表示该区间可用的区间的最大长度,
至于这个tlen区间的具体位置在哪里不知道,只是知道该区间内存在这么一段可用的区间,并且注意,这个tlen表示的是最大长度,该节点可能有多段可用的区间,但是最长的长度是tlen
记录了这个信息,至少能解决一个问题,就是能不能找到一个合适的区间。如果查询的区间长度W > 总区间的tlen,那么查询一定是失败的(总区间中可以的最大区间都不能满足那就肯定失败)
但这远远不够,其一查询是要返回区间的具体位置的,这里无法返回位置,另外是要查询最左区间,最左的且满足>=W的区间可能不是这个tlen区间

那么我们进一步思考这个问题
首先我们先增加两个域,llen,rlen
llen表示一个区间从最左端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[0,0,0,1,1],llen = 3,从最左端有3格可以利用
区间[1,5],覆盖情况为[1,0,0,0,0],llen = 0,因为从最左端开始找不到1格可用的区间
rlen表示一个区间从最右端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[1,0,1,0,0],rlen = 2,从最右端有2格可以利用
区间[1,5],覆盖情况为[0,0,0,0,1],rlen = 0,因为从最右端开始找不到1格可用的区间
对于一个区间,我们知道它左半区间的tlen,和右半区间的tlen,如果左半区间的tlen >= W ,那么我们一定能在左边找到(满足最左),所以可以深入到左半区间去确定该区间的具体位置
如果左端的不满足,那么我们要先考虑横跨两边的区间(因为要满足最左),因而记录的llen,rlen可以派上用场,一段横跨的区间,
那么是 左边区间rrlen + 右边区间llen ,如果满足的话,就是该区间了,它的位置也是可以确定的
如果横跨的区间不满足,那么就在右半区间找,如果右半区间的tlen >= W , 那么可以在右半区间找到,所以深入到右半区间去确定它的具体位置,否则的话,整个查询就失败了

可见查询是建立在tlen,llen,rlen这个信息之上的,而每次查询后其实伴随着修改,而且还有专门的修改操作,这些修改操作都会改变tlen,llen,rlen的值,所以在更新的时候是时刻维护这些信息

关于这3个信息的维护

当前区间的tlen = max{ 左半区间tlen , 右半区间tlen , 左半区间rlen+右半区间llen} (这个不难理解吧,取左右较大的那个,或者横跨中间的那个)

如果左半区间全部可以用: 当前区间llen = 左半区间llen(tlen) + 右半区间llen 
左半区间部分能用: 当前区间llen = 左半区间llen

如果右半区间全部能用: 当前区间rlen = 右半区间rlen(tlen) + 左半区间rlen
右半区间部分能用: 当前区间rlen = 右半区间rlen

这样就全部维护好了

代码学习了小HH的代码风格

楼主太懒,没心思写这个代码了。

体谅一下,贴了https://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html

  1 #include <cstdio>
  2 #include <cstring>
  3 #define lch(i) ((i)<<1)
  4 #define rch(i) ((i)<<1|1)
  5 #define max(a,b) ((a)>(b)?(a):(b))
  6 #define min(a,b) ((a)<(b)?(a):(b))
  7 #define N  50010
  8 #define INF  0x3f3f3f3f
  9
 10 struct node
 11 {
 12     int l,r;
 13     int mark;
 14     int tlen,llen,rlen;
 15     int mid(){
 16         return (l+r)>>1;
 17     }
 18     int cal_len(){
 19         return r-l+1;
 20     }
 21     void updata_len(){
 22         tlen = llen = rlen = ( mark ? 0 : cal_len() );
 23     }
 24 }t[4*N];
 25
 26 void build(int l ,int r ,int rt)
 27 {
 28     t[rt].l = l; t[rt].r = r;
 29     t[rt].tlen = t[rt].llen = t[rt].rlen = t[rt].cal_len();
 30     t[rt].mark = 0;
 31     if(l == r) return ;
 32     int mid = t[rt].mid();
 33     build(l , mid , lch(rt));
 34     build(mid+1 , r , rch(rt));
 35     return ;
 36 }
 37
 38 int query(int w ,int rt)
 39 {
 40     if(t[rt].l == t[rt].r && w == 1) //叶子特判
 41         return t[rt].l;
 42     if(t[rt].mark != -1) //延迟标记,父亲信息传递给儿子
 43     {
 44         t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark;
 45         t[rt].mark = -1;
 46         t[lch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况
 47         t[rch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况
 48     }
 49     if(t[lch(rt)].tlen >= w) //左孩子的可用区间可以满足,那么一定在左孩子区间内
 50         return query(w , lch(rt));
 51     else if(t[lch(rt)].rlen + t[rch(rt)].llen >= w) //横跨左右孩子且连续的区间可以满足,那么可以直接返回下标
 52         return ( t[lch(rt)].r - t[lch(rt)].rlen + 1 );
 53     else if(t[rch(rt)].tlen >= w) //右孩子的可用区间可以满足,那么去右孩子处找
 54         return query(w , rch(rt));
 55     else //找不到可用的区间
 56         return 0;
 57 }
 58
 59 void updata(int l ,int r ,int val ,int rt)
 60 {
 61     if(t[rt].l == l && t[rt].r == r)
 62     {
 63         t[rt].mark = val;
 64         t[rt].updata_len();
 65         return ;
 66     }
 67     if(t[rt].mark != -1) //延迟标记,父亲信息传递给儿子
 68     {
 69         t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark;
 70         t[rt].mark = -1;
 71         t[lch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况
 72         t[rch(rt)].updata_len(); //传递信息后更新孩子的区间覆盖情况
 73     }
 74     int mid = t[rt].mid();
 75     if(l > mid) //修改的区间在右孩子
 76         updata(l , r , val , rch(rt));
 77     else if(r <= mid) //修改的区间在左孩子
 78         updata(l , r , val , lch(rt));
 79     else
 80     {
 81         updata(l , mid , val , lch(rt));
 82         updata(mid+1 , r , val , rch(rt));
 83     }
 84     int tmp = max(t[lch(rt)].tlen , t[rch(rt)].tlen);
 85     t[rt].tlen = max(tmp , t[lch(rt)].rlen + t[rch(rt)].llen);
 86     t[rt].llen = t[lch(rt)].llen;
 87     t[rt].rlen = t[rch(rt)].rlen;
 88     if(t[lch(rt)].tlen == t[lch(rt)].cal_len() )
 89         t[rt].llen += t[rch(rt)].llen;
 90     if(t[rch(rt)].tlen == t[rch(rt)].cal_len() )
 91         t[rt].rlen += t[lch(rt)].rlen;
 92     return ;
 93 }
 94
 95 int main()
 96 {
 97     int n,m;
 98     scanf("%d%d",&n,&m);
 99     build(1,n,1);
100     while(m--)
101     {
102         int choose;
103         scanf("%d",&choose);
104         if(choose == 1) //查询操作
105         {
106             int w;
107             scanf("%d",&w);
108             int index = query(w,1);
109             printf("%d\n",index);
110             if(index)
111                 updata(index , index+w-1 , 1 , 1);
112         }
113         else
114         {
115             int l,len;
116             scanf("%d%d",&l,&len);
117             updata(l , l+len-1 , 0 , 1);
118         }
119     }
120     return 0;
121 }
时间: 2024-10-12 18:12:42

poj 3667 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(线段树区间合并)

http://poj.org/problem?id=3667 题意:两个操作 : 1 选出靠左的长度为a的区间. 2 把从 a到a+b的区间清空. 线段树区间合并+lazy // by caonima // hehe #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; co

poj 3667 Hotel - 线段树

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 Street as their v

POJ 1823 Hotel 线段树

题目链接 和上一题差不多....第三种操作只需要输出maxx[1]的值就可以. #include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set> #include <string>

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

题目传送门 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——————【线段树区间合并】

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