poj 3667 线段树

来源:互联网 发布:图片融合软件 编辑:程序博客网 时间:2024/05/22 07:48

题目:

Hotel
Time Limit: 3000MS Memory Limit: 65536KTotal Submissions: 16772 Accepted: 7297

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 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 r to 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 61 31 31 31 32 5 51 6

Sample Output

14705

n个房间,m个操作,1表示集体订房,要求连续,输出符合要求的最小起始房间编号;2表示退房。


分析:

线段树区间更新,核心在于维护从一端开始的最大连续区间。查询时分三种情况:左孩子空房间足够,左右连起来够,右孩子空房间够。


贴一下菊苣的思路:

(https://www.carlstedt.cn/archives/687) Orz

分析问题本质:

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

这样就全部维护好了


代码:

#include<iostream>#include<cstring>#include<cstdio>#include<cstdlib>#include<ctype.h>    //tower()#include<set>  #include<map>  #include<iomanip>// cout<<setprecision(1)<<fixed<<a;#include<vector>   #include<time.h>  #include<assert.h>  //assert#include<cmath>#include<algorithm>#include<bitset>#include<limits.h>#include<stack>#include<queue>using namespace std;const int maxn=50010;const int inf=INT_MAX;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1/*核心在于维护从一端开始的最大连续区间*/int add[maxn<<2],sum[maxn<<2];//维护当前节点最大连续区间长度 int lsum[maxn<<2],rsum[maxn<<2];   //从区间左右开始的最大连续区间长度 int n,m;void pushup(int rt,int ans){lsum[rt]=lsum[rt<<1];rsum[rt]=rsum[rt<<1|1];//....全部未占用,则继续向旁边拓展 if(lsum[rt]==ans-(ans>>1)) lsum[rt]+=lsum[rt<<1|1];if(rsum[rt]==(ans>>1)) rsum[rt]+=rsum[rt<<1];sum[rt]=max(max(sum[rt<<1],sum[rt<<1|1]),lsum[rt<<1|1]+rsum[rt<<1]);}void pushdown(int rt,int ans){if(add[rt]!=-1){add[rt<<1]=add[rt<<1|1]=add[rt];sum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=add[rt]?0:ans-(ans>>1);sum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=add[rt]?0:(ans>>1);add[rt]=-1;}}void build(int l,int r,int rt){sum[rt]=lsum[rt]=rsum[rt]=r-l+1;add[rt]=-1;if(l==r) return;int m=(l+r)>>1;build(lson);build(rson);}void update(int a,int b,int c,int l,int r,int rt){//c=1,占用 c=0,退房 if(a<=l&&b>=r){sum[rt]=lsum[rt]=rsum[rt]=c?0:(r-l+1);add[rt]=c;return;}pushdown(rt,r-l+1);int m=(l+r)>>1;if(a<=m) update(a,b,c,lson);if(b>m) update(a,b,c,rson);pushup(rt,r-l+1);} int query(int res,int l,int r,int rt){//返回下标值 if(l==r) return l;pushdown(rt,r-l+1);int m=(l+r)>>1;if(sum[rt<<1]>=res) return query(res,lson);else if((rsum[rt<<1]+lsum[rt<<1|1])>=res) return m-rsum[rt<<1]+1;//左子树右侧+右子树左侧 return query(res,rson);}int main(){while(scanf("%d%d",&n,&m)==2){build(1,n,1);int t,a,b;while(m--){scanf("%d",&t);if(t==1){//查询+更新 scanf("%d",&a);if(sum[1]<a) puts("0");else{int q=query(a,1,n,1);printf("%d\n",q);update(q,q+a-1,1,1,n,1);}}else{//更新 scanf("%d%d",&a,&b);update(a,a+b-1,0,1,n,1);}}}return 0;}



0 0
原创粉丝点击