线段树维护连续区间最大长度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 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 Di (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

题意:(Thanks for wust_Li Shunxin)

这座巨型宾馆在一条超长走廊上有N(1 ≤ N ≤ 50000)个排成一排的房间,每个房间都能欣赏到苏必利尔湖的好景色。现在所有的房间都是空的。

现在Bessie等旅客们正在不断地发出订房和退房要求。你需要接受M(1 ≤ M < 50000)条指令:

每条指令的第一个数字为1或2。如果是1,后面将有一个整数D表示顾客要预定的房间数。注意,这些房间必须是连续的。如果能够满足旅客的订房要求, 输出满足要求的第一个房间的编号(例如,要订房6间,输出3表示3, 4, 5, 6, 7, 8是满足要求的),这样的编号必须是可能的编号里面最靠前的。如果不能满足要求,输出0。

如果是2,后面将有两个整数X和D表示顾客要退掉X, X + 1, X + 2, ... , X + D - 1这D间房。对于这样的指令什么都不输出
线段树保存的信息有,cover代表是否有人,msum代表区间内最大的连续长度,lsum是从左结点往右的连续长度,rsum是从右结点往左的连续长度。

做法用一个线段树维护最大区间的长度;

每个点有个sum表示连续区间的最大长度,lsum表示从左边连续区间的长度,rsum表示从右边连续区间的长度

tag表示标记,初始为-1;0表示空,1表示满;

主要是区间合并操作要注意更新lsum,和rsum,以及根据lsum,rsum如何得到sum

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<set>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<cmath>
using namespace std;
const int INF=0x7f7f7f7f;
const int maxn=5e4+5;
struct node
{
    int sum;//能住进人的数量
    int lsum;//左边最大的连续区间
    int rsum;//右边最大的连续区间
    int tag;//标记0表示没住人,-1是初始状态,1表示住人了
}b[maxn<<2];
void build (int l,int r,int rt)
{
    b[rt].lsum=b[rt].rsum=b[rt].sum=r-l+1;
    b[rt].tag=-1;
    if(l==r)
    {
        b[rt].sum=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
void down(int l,int r,int rt)
{
    if(b[rt].tag!=-1)//这个区间已经被置为空了
    {
        b[rt<<1].tag=b[rt<<1|1].tag=b[rt].tag;//标记向下传递
        int mid=(l+r)>>1;
        if(b[rt].tag)//满,区间长度赋为0
        {
            b[rt<<1].sum=b[rt<<1].lsum=b[rt<<1].rsum=0;
            b[rt<<1|1].sum=b[rt<<1|1].lsum=b[rt<<1|1].rsum=0;

        }
        else//空,所有长度赋维最大
        {
            b[rt<<1].sum=b[rt<<1].lsum=b[rt<<1].rsum=(mid-l+1);
            b[rt<<1|1].sum=b[rt<<1|1].lsum=b[rt<<1|1].rsum=(r-mid);

        }
        b[rt].tag=-1;
    }
}
void up(int l,int r,int rt)//区间合并
{
    b[rt].lsum=b[rt<<1].lsum;
    b[rt].rsum=b[rt<<1|1].rsum;
    int mid=(l+r)>>1;
    if(b[rt].lsum==mid-l+1) //左区间为空
    {
        b[rt].lsum+=b[rt<<1|1].lsum;//扩大左区间,和右子树的左区间合并
    }
    if(b[rt].rsum==r-mid)
    {
        b[rt].rsum+=b[rt<<1].rsum;
    }
    b[rt].sum=max(b[rt<<1].rsum+b[rt<<1|1].lsum,max(b[rt<<1].sum,b[rt<<1|1].sum));
}
void update(int l,int r,int rt,int ll,int rr,int c)//相当于是修改区间,c是目标值
{
    if(ll<=l&&r<=rr)
    {
        if(c==0)//置空
        {
            b[rt].lsum=b[rt].rsum=b[rt].sum=(r-l+1);
            b[rt].tag=0;
        }
        else if(c==1)//住进人
        {
            b[rt].lsum=b[rt].rsum=b[rt].sum=0;
            b[rt].tag=1;
        }
        return ;
    }
    down(l,r,rt);
    int mid=(l+r)>>1;
    if(ll<=mid) update(l,mid,rt<<1,ll,rr,c);
    if(rr>mid)  update(mid+1,r,rt<<1|1,ll,rr,c);
    up(l,r,rt);
}
int query(int l,int r,int rt,int num)//查询适合放置的区间
{
    if(l==r) return l;
    down(l,r,rt);
    int mid=(l+r)>>1;
    if(b[rt<<1].sum>=num) return query(l,mid,rt<<1,num);//左边一个区间有
    else if(b[rt<<1].rsum+b[rt<<1|1].lsum>=num) return  mid-b[rt<<1].rsum+1;//横跨两段
    else if(b[rt<<1|1].sum>=num) return query(mid+1,r,rt<<1|1,num);//往右找
}
int n,m;
int x,y,op;

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        while(m--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d",&x);
                if(x>b[1].sum)
                {
                    puts("0");
                }
                else
                {
                    int p=query(1,n,1,x);
                    cout<<p<<endl;
                    update(1,n,1,p,x+p-1,1);
                }
            }
            else if(op==2)
            {
                scanf("%d%d",&x,&y);
                update(1,n,1,x,x+y-1,0);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codetypeman/article/details/81308909