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

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

一个线段,n 有m个操作,

1 y  查询操作,是不是有一个连续的y个点,输出最左边的点,然后y个点被占用。如果没有就输出0;

2 x y,修改操作,从x点开始,连续y个点别释放。


这个题就是一个区间线段树。要注意修改数据时的情况。

三个变量。 len llen rlen,

关于这3个信息的维护

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

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

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

这样就全部维护好了

具体看   链接 

这个程序中在结构体中写了 三个函数, 

分别是 mid 函数,求区间长函数,赋值函数,,

有了这些函数,有些代码就不需要过多的重复。

这个是修改过的代码,看起来比之前的简洁了一些。

#include <cstdio>
#include <algorithm>
using  namespace std;
struct node{
    int l,r,a,b,llen,rlen,len,c;//l 左孩子,r 右孩子,llen,rlen,len,从左最多空房间,从右的空房间,最大的连续空房间。
    int mid(){
        return (a + b)/2;
    }
    int cal_len(){
        return b-a;
    }
    void updata_len(){
        len = llen = rlen = c*cal_len();
    }
}f[100500];
int n,x,y,z,m,t;
void updata(int p){
    if (f[p].c == -1) return;
    f[f[p].l].c = f[f[p].r].c = f[p].c;
    f[p].c = -1;
    f[f[p].l].updata_len();
    f[f[p].r].updata_len();
    return;
}
void build(int p,int a, int b){
    f[p].a = a; f[p].b = b;
    f[p].c = -1;
    f[p].len = f[p].llen = f[p].rlen = f[p].cal_len();
    if (f[p].a + 1 == f[p].b) {
        return ;
    }
    int mid = f[p].mid();
    t++; f[p].l = t; build(t,a,mid);
    t++; f[p].r = t; build(t,mid,b);
    return;
}
int Qurey(int p){
    if (f[p].len < y) return 0;
    if (f[p].a+1==f[p].b && y == 1) return f[p].a; //到叶子节点,返回。
    updata(p);
    if (f[f[p].l].len >= y) return Qurey(f[p].l); else //查左区间
        if (f[f[p].l].rlen+f[f[p].r].llen >= y) return f[f[p].l].b-f[f[p].l].rlen; else //左区间的右,和右区间的左
            if (f[f[p].r].len >= y) return Qurey(f[p].r); else //右区间。
                return 0; //查不到,返回0;
}
void updown(int p){  // 这个是维护区间的值。下面的区间改了,父亲节点也要改。
    f[p].llen = f[f[p].l].llen;
    if (f[f[p].l].llen == f[f[p].l].b-f[f[p].l].a) f[p].llen += f[f[p].r].llen;
    f[p].rlen = f[f[p].r].rlen;
    if (f[f[p].r].rlen == f[f[p].r].b-f[f[p].r].a) f[p].rlen += f[f[p].l].rlen;
    f[p].len = max(f[f[p].l].len,max(f[f[p].r].len,f[f[p].l].rlen+f[f[p].r].llen));
    return ;
}
void Insert(int p){ //这个是修改区间。
    if (x <= f[p].a && y >= f[p].b-1){
        f[p].c = z;
        f[p].updata_len();
        return;
    }
    updata(p); //lazy 标记,向下扩展。
    int mid = f[p].mid();
    if (x < mid) Insert(f[p].l);
    if (y >= mid) Insert(f[p].r);
    updown(p); //维护区间的值。
    return ;
}
int main() {
    int zz;
    scanf("%d%d",&n,&m);
    t = 1;
    build(1,1,n+1);
    for (int i = 0; i < m; i++){
        scanf("%d",&zz);
        if (zz == 1) {
            scanf("%d",&y);
            x = Qurey(1);
            y = y + x -1;
            z = 0;
            printf("%d\n",x);
            if (x) Insert(1); //占用房间,所以 z 设为0.
        } else{
            scanf("%d%d",&x,&y);
            z = 1;
            y = x + y -1;
            Insert(1); //释放房间,所以 z 设为1.
        }
    }
    return 0;
}

这个是一开始写的代码,显得有一些啰嗦。

#include <cstdio>
#include <algorithm>
using  namespace std;
struct node{
    int l,r,a,b,llen,rlen,len,c;//l 左孩子,r 右孩子,llen,rlen,len,从左最多空房间,从右的空房间,最大的连续空房间。
}f[500500];
int n,x,y,z,m,t;
void updata(int p){
    if (f[p].c == -1) return;
    f[f[p].l].len = (f[f[p].l].b-f[f[p].l].a)*f[p].c;
    f[f[p].l].llen = f[f[p].l].len;
    f[f[p].l].rlen = f[f[p].l].len;
    f[f[p].r].len = (f[f[p].r].b-f[f[p].r].a)*f[p].c;
    f[f[p].r].llen = f[f[p].r].len;
    f[f[p].r].rlen = f[f[p].r].len;
    f[f[p].l].c = f[p].c;
    f[f[p].r].c = f[p].c;
    f[p].c = -1;
    return;
}
void build(int p,int a, int b){
    f[p].a = a; f[p].b = b;
    if (a+1 == b){
        f[p].len = 1; // 1 代表这个房间是空的,所以加起来的数就是空房间的数量。
        f[p].llen = 1;//一开始,从左,从右,总的,都是一样的。
        f[p].rlen = 1;
        f[p].c = -1;//lazy 标记点。1 代表是空的,0 代表这个房间被占用,
        return;
    }
    int mid = (a+b)/2;
    t++; f[p].l = t; build(t,a,mid);
    t++; f[p].r = t; build(t,mid,b);
    f[p].len = f[f[p].l].len + f[f[p].r].len;
    f[p].llen = f[p].len;
    f[p].rlen = f[p].len;
    f[p].c = -1;
    return;
}
int Qurey(int p){
    if (f[p].len < y) return 0;
    if (f[p].a+1==f[p].b && y == 1) return f[p].a; //到叶子节点,返回。
    updata(p);
    if (f[f[p].l].len >= y) return Qurey(f[p].l); else //查左区间
        if (f[f[p].l].rlen+f[f[p].r].llen >= y) return f[f[p].l].b-f[f[p].l].rlen; else //左区间的右,和右区间的左
            if (f[f[p].r].len >= y) return Qurey(f[p].r); else //右区间。
                return 0; //查不到,返回0;
}
void updown(int p){  // 这个是维护区间的值。下面的区间改了,父亲节点也要改。
    f[p].llen = f[f[p].l].llen;
    if (f[f[p].l].llen == f[f[p].l].b-f[f[p].l].a) f[p].llen += f[f[p].r].llen;
    f[p].rlen = f[f[p].r].rlen;
    if (f[f[p].r].rlen == f[f[p].r].b-f[f[p].r].a) f[p].rlen += f[f[p].l].rlen;
    f[p].len = max(f[f[p].l].len,max(f[f[p].r].len,f[f[p].l].rlen+f[f[p].r].llen));
    return ;
}
void Insert(int p){ //这个是修改区间。
    if (x <= f[p].a && y >= f[p].b-1){
        f[p].len = (f[p].b-f[p].a)*z;
        f[p].llen = f[p].len;
        f[p].rlen = f[p].len;
        f[p].c = z;
        return;
    }
    updata(p); //lazy 标记,向下扩展。
    int mid = (f[p].a+f[p].b)/2;
    if (x < mid) Insert(f[p].l);
    if (y >= mid) Insert(f[p].r);
    updown(p); //维护区间的值。
    return ;
}
int main() {
    int zz;
    scanf("%d%d",&n,&m);
    t = 1;
    build(1,1,n+1);
    for (int i = 0; i < m; i++){
        scanf("%d",&zz);
        if (zz == 1) {
            scanf("%d",&y);
            x = Qurey(1);
            y = y + x -1;
            z = 0;
            printf("%d\n",x);
            if (x) Insert(1); //占用房间,所以 z 设为0.
        } else{
            scanf("%d%d",&x,&y);
            z = 1;
            y = x + y -1;
            Insert(1); //释放房间,所以 z 设为1.
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/80515006