Hotel--POJ3667(線段樹簡單區間合併詳解)

Hotel

Time Limit: 3000MS   Memory Limit: 65536K

題目鏈接http://poj.org/problem?id=3667

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 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

題目大意:n個連續的房間m個操作。操作分兩種,第一種以1 x形式給出,找到最左的能連續容下x個人的連續房間,並輸出左端點的編號,如果找不到就輸出0.第二種以2 l x的形式給出,表示以l爲起點的x個房間都清空。

線段樹區間合併的板子題。。。對剛開始觸及到的萌新還是稍微提一下:

我們設置變量lsum,rsum,sum,f分別表示節點從左端點開始最大的連續區間,從右端點開始的連續區間,整段區間的最大連續區間,標記。

區間和並簡單來講就是當一個節點的左兒子的左端點最大連續區間==區間長度的時候,我們將該節點的lsum賦值爲左兒子的區間長度+右兒子的左端點開始的最大連續區間,同樣的道理,右兒子的右端點最大連續區間==區間長度的時候也是一樣的操作:

if (tree[ls].lsum==(mid-l+1)) tree[rt].lsum=tree[ls].sum+tree[rs].lsum;//區間合併
if (tree[rs].rsum==(r-mid)) tree[rt].rsum=tree[rs].sum+tree[ls].rsum;//

事實上,區間合併就是這兩行,其他的都是線段樹的操作了。。。就看你怎麼維護這些變量了

更新操作的時候,其他的都好辦,和區間染色一樣,f直接更新,這裏就稍微提一下下傳標記和上傳值。下傳標記我們和區間染色一樣是直接覆蓋的,那麼我們左右兒子的值就直接等於父親的標記*區間長度。上傳值的時候我們就需要區間合併了,不過在這之前我們還應該普通更新一下,也就是該節點的sum的值是又左右兒子的sum或者左右兒子合併之後值得來的,那麼也就是說:sum=max(max(ls->sum,rs->sum),ls->rsum+rs->lsum)。那麼他的lsum和rsum可以直接由左兒子的左端點和右兒子的右端點直接轉移過來:

void pushup(int l,int r,int rt)
{
    int mid=(l+r)>>1;
	tree[rt].sum=max(max(tree[ls].sum,tree[rs].sum),tree[ls].rsum+tree[rs].lsum);
    tree[rt].lsum=tree[ls].lsum;tree[rt].rsum=tree[rs].rsum;
    if (tree[ls].lsum==(mid-l+1)) tree[rt].lsum=tree[ls].sum+tree[rs].lsum;//區間合併 
    if (tree[rs].rsum==(r-mid)) tree[rt].rsum=tree[rs].sum+tree[ls].rsum;//
}


void pushdown(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    tree[ls].sum=tree[ls].rsum=tree[ls].lsum=tree[rt].f*(mid-l+1);
    tree[rs].sum=tree[rs].rsum=tree[rs].lsum=tree[rt].f*(r-mid);
    tree[ls].f=tree[rs].f=tree[rt].f;
    tree[rt].f=-1;
}

查詢的時候我們要怎麼找呢?我們先判斷一下該節點的sum值是否大於等於所需要的空間,如果滿足的話就開始判斷了,否則直接返回

0。我們從小開始找,那麼我們先判斷左兒子的sum是否>=size,如果ok的話直接遞歸就不管了,否則的話,判斷合併之後是否中間的合併區間>=size,即tree[ls].rsum+tree[rs].lsum>=size,如果ok的話我們就可以直接確定最小編號了,爲mid-tree[ls].rsum+1。然後我們繼續找右區間,和左區間一樣的寫法就不多說了:

int query(int l,int r,int rt,int siz)
{
    if (l==r && siz==1) return l; //特判
    int mid=(l+r)>>1;
    if (tree[rt].f!=-1) pushdown(l,r,rt);
    if (tree[rt].sum>=siz){
    	if (tree[ls].sum>=siz) return query(lson,siz);
    	else if (tree[ls].rsum+tree[rs].lsum>=siz) return mid-tree[ls].rsum+1;
		else return query(rson,siz); 
	}
	return 0;
}

如果上面理解了的話,下面的AC代碼也可以自己寫了。

以下是AC代碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1

const int mac=5e4+10;

struct node
{
    int lsum,rsum,sum;
    int f;
}tree[mac<<2];

void pushup(int l,int r,int rt)
{
    int mid=(l+r)>>1;
	tree[rt].sum=max(max(tree[ls].sum,tree[rs].sum),tree[ls].rsum+tree[rs].lsum);
    tree[rt].lsum=tree[ls].lsum;tree[rt].rsum=tree[rs].rsum;
    if (tree[ls].lsum==(mid-l+1)) tree[rt].lsum=tree[ls].sum+tree[rs].lsum;//區間合併 
    if (tree[rs].rsum==(r-mid)) tree[rt].rsum=tree[rs].sum+tree[ls].rsum;//
}

void build(int l,int r,int rt)
{
    tree[rt].f=-1;
    tree[rt].lsum=tree[rt].rsum=tree[rt].sum=(r-l+1);
    //printf ("%d %d :%d %d %d\n",l,r,tree[rt].lsum,tree[rt].sum,tree[rt].rsum);
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lson);build(rson);
}

void pushdown(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    tree[ls].sum=tree[ls].rsum=tree[ls].lsum=tree[rt].f*(mid-l+1);
    tree[rs].sum=tree[rs].rsum=tree[rs].lsum=tree[rt].f*(r-mid);
    tree[ls].f=tree[rs].f=tree[rt].f;
    tree[rt].f=-1;
}

void update(int l,int r,int rt,int L,int R,int val)
{
    if (l>=L && r<=R){
        tree[rt].lsum=tree[rt].rsum=tree[rt].sum=(r-l+1)*val;
        tree[rt].f=val;
        return;
    }
    if (tree[rt].f!=-1) pushdown(l,r,rt);
    int mid=(l+r)>>1;
    if (mid>=L) update(lson,L,R,val);
    if (mid<R) update(rson,L,R,val);
    pushup(l,r,rt);
}

int query(int l,int r,int rt,int siz)
{
    if (l==r && siz==1) return l; 
    int mid=(l+r)>>1;
    if (tree[rt].f!=-1) pushdown(l,r,rt);
    if (tree[rt].sum>=siz){
    	if (tree[ls].sum>=siz) return query(lson,siz);
    	else if (tree[ls].rsum+tree[rs].lsum>=siz) return mid-tree[ls].rsum+1;
		else return query(rson,siz); 
	}
	return 0;
}

int main()
{
    //freopen("in.txt","r",stdin);
	int n,m;
    scanf ("%d%d",&n,&m);
    build(1,n,1);
    for (int i=1; i<=m; i++){
        int opt,x,d;
        scanf ("%d",&opt);
        if (opt==1){
            scanf ("%d",&d);
            int ans=query(1,n,1,d);
            printf ("%d\n",ans);
            if (!ans) continue;
            update(1,n,1,ans,ans+d-1,0);
        }
        else {
            scanf ("%d%d",&x,&d);
            update(1,n,1,x,x+d-1,1);
        }
    }
    return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章