1. 程式人生 > >Hotel——線段樹

Hotel——線段樹

Hotel
題目描述

奶牛們最近的旅遊計劃,是到蘇必利爾湖畔,享受那裡的湖光山色,以及明媚的陽光。作為整個旅遊的策劃者和負責人,貝茜選擇在湖邊的一家著名的旅館住宿。這個巨大的旅館一共有N (1 <= N <= 50,000)間客房,它們在同一層樓中順次一字排開,在任何一個房間裡,只需要拉開窗簾,就能見到波光粼粼的湖面。

貝茜一行,以及其他慕名而來的旅遊者,都是一批批地來到旅館的服務檯,希望能訂到D_i (1 <= D_i <= N)間連續的房間。服務檯的接待工作也很簡單:如果存在r滿足編號為r..r+D_i-1的房間均空著,他就將這一批顧客安排到這些房間入住;如果沒有滿足條件的r,他會道歉說沒有足夠的空房間,請顧客們另找一家賓館。如果有多個滿足條件的r,服務員會選擇其中最小的一個。

旅館中的退房服務也是批量進行的。每一個退房請求由2個數字X_i、D_i描述,表示編號為X_i..X_i+D_i-1 (1 <= X_i <= N-D_i+1)房間中的客人全部離開。退房前,請求退掉的房間中的一些,甚至是所有,可能本來就無人入住。

而你的工作,就是寫一個程式,幫服務員為旅客安排房間。你的程式一共需要處理M (1 <= M < 50,000)個按輸入次序到來的住店或退房的請求。第一個請求到來前,旅店中所有房間都是空閒的。

輸入

  • 第1行: 2個用空格隔開的整數:N、M

  • 第2..M+1行: 第i+1描述了第i個請求,如果它是一個訂房請求,則用2個數字: 1、D_i描述,數字間用空格隔開;
    如果它是一個退房請求,用3個以空格隔開的數字: 2、X_i、D_i描述

輸出

  • 第1..??行: 對於每個訂房請求,輸出1個獨佔1行的數字:如果請求能被滿足,輸出滿足條件的最小的r;如果請求無法被滿足,輸出0

樣例輸入

10 6
1 3
1 3
1 3
1 3
2 5 5
1 6

樣例輸出
1
4
7
0
5

一開始呢,是不會做這道題的。。。對於在一個區間內“縫縫補補”(染色)之後來找連續的一個長度限定的(len>=d)這樣一個區間,是比較陌生的。

然後呢,可愛的ljh大神就描述了一下他的做法,對於每個節點,有lmax,rmax,maxx三個域,分別表示當前區間左邊連續的最長子區間,右邊開始連續的最長子區間,以及整個區間中的最長子區間。

這樣一想,就知道怎麼insert了,關鍵就在要把當前區間i的i*2和i*2+1兩個子區間的中間連起來:tree[i*2].rmax+tree[i*2+1].lmax
還有用tree[i].hotel做一個lazy-tag,延後染色,一切都那麼和諧。

但是在尋找x(最近的連續長度超過d的區間的左端點)時,掛了。
詳細的呢,看註釋吧。

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 50000
struct node{ 
    int l,r;
    int lmax,rmax,maxx,pos;
    int hotel;
}tree[MAXN*4+100];
int n,m;
void Max(int x,int y,node &z,int xpos,int ypos)
{
    if(x<y)
        z.maxx=y,z.pos=ypos;
    else
        z.maxx=x,z.pos=xpos;
}
void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r;
    tree[i].lmax=tree[i].rmax=tree[i].maxx=r-l+1;
    tree[i].pos=l;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
}
void cover(node &x,int len,int pos)
{
    x.lmax=x.rmax=x.maxx=len;
    x.pos=pos;
}
void insert(int i,int l,int r,int f)
{
    if(r<tree[i].l || tree[i].r<l) return ;
    if(l<=tree[i].l && tree[i].r<=r){
        tree[i].hotel=f;
        if(f)
            cover(tree[i],0,-1);
        else
            cover(tree[i],tree[i].r-tree[i].l+1,tree[i].l);
        return ; 
    }
    if(tree[i].hotel>=0){ //lazy-tag
        tree[i*2].hotel=tree[i*2+1].hotel=tree[i].hotel;
        if(tree[i].hotel){
            cover(tree[i*2],0,-1);
            cover(tree[i*2+1],0,-1);
        }
        else{
            cover(tree[i*2],tree[i*2].r-tree[i*2].l+1,tree[i*2].l);
            cover(tree[i*2+1],tree[i*2+1].r-tree[i*2+1].l+1,tree[i*2+1].l);
        }
    }
    tree[i].hotel=-1;
    insert(i*2,l,r,f);
    insert(i*2+1,l,r,f);

    int mid=(tree[i].l+tree[i].r)/2;
    tree[i].lmax=tree[i*2].lmax,tree[i].rmax=tree[i*2+1].rmax;
    if(tree[i].lmax==tree[i*2].r-tree[i*2].l+1)
        tree[i].lmax+=tree[i*2+1].lmax;
    if(tree[i].rmax==tree[i*2+1].r-tree[i*2+1].l+1)
        tree[i].rmax+=tree[i*2].rmax;

    Max(tree[i*2].maxx,tree[i*2+1].maxx,tree[i],tree[i*2].pos,tree[i*2+1].pos);
    Max(tree[i].maxx,tree[i*2].rmax+tree[i*2+1].lmax,tree[i],tree[i].pos,mid-tree[i*2].rmax+1);
    Max(tree[i].maxx,tree[i*2].lmax,tree[i],tree[i].pos,tree[i].l);
    Max(tree[i].maxx,tree[i*2+1].rmax,tree[i],tree[i].pos,tree[i].r);
}
int find_room(int i,int d)
{
    if(tree[i].maxx<d) return -1;  //沒有任一區間滿足長度超過d的空閒區間
    if(tree[i].l==tree[i].r) return tree[i].l;   //important
    if(tree[i].lmax>=d) return tree[i].l;  //都是被迫遞迴下來的,能有區間左端點就是最好的了
    if(tree[i*2].maxx>=d) return find_room(i*2,d); //左區間又肯定在左區間找
    else if(tree[i*2].rmax+tree[i*2+1].lmax>=d)  //左區間不夠,但是中間那一截夠了也是不錯的,至少比右區間的要近些
        return tree[i*2].r-tree[i*2].rmax+1;
    else return find_room(i*2+1,d); //實在不行了,去右區間。。。
}
int main()
{
    int flag,d,x;
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d",&flag);
        if(flag==1){
            scanf("%d",&d);
            x=find_room(1,d);
            if(x==-1)
                printf("0\n");
            else{
                printf("%d\n",x);
                insert(1,x,x+d-1,1);
            }
        }
        else{
            scanf("%d%d",&x,&d);
            insert(1,x,x+d-1,0);
        }
    }
}