poj 3667 Hotel


推薦技術公眾號:不愛睡覺的大豬

 

線段樹 

題意:有一個線段,從1到n,下面m個操作,操作分兩個類型,以1開頭的是查詢操作,以2開頭的是更新操作

1 w  表示在總區間內查詢一個長度為w的可用區間,並且要最靠左,能找到的話返回這個區間的左端點並占用了這個區間,找不到返回0 

好像n=10 , 1 3 查到的最左的長度為3的可用區間就是[1,3],返回1,並且該區間被占用了

2 a len , 表示從單位a開始,清除一段長度為len的區間(將其變為可用,不被占用),不需要輸出

因此看sample的話就可以理解了

 

記錄一下自己的感悟:

用線段樹,首先要定義好線段樹的節點信息,一般看到一個問題,很難很快能確定線段樹要記錄的信息
做線段樹不能為了做題而做,首先線段樹是一種輔助結構,它是為問題而生的,因而必須具體問題具體分析
回憶一下RMQ問題,其實解決RMQ有很多方法,根本不需要用到線段樹,用線段樹解決RMQ,其實是利用線段樹的性質來輔助解決這個問題
回憶一下求矩形面積並或周長並的問題,一般使用的是掃描線法,其實掃描線法和線段樹一點關系都沒有,掃描線法應該歸為計算幾何的算法,
使用線段樹只是為了輔助實現掃描線法

因而回到這題,要解,必須分析問題本質,才去思考怎么用線段樹來輔助,另外為什么能用線段樹輔助是可行的,這個問題似乎更有價值

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

這樣就全部維護好了

 

代碼學習了小HH的代碼風格

#include <cstdio>
#include <cstring>
#define lch(i) ((i)<<1)
#define rch(i) ((i)<<1|1)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define N  50010
#define INF  0x3f3f3f3f

struct node
{
    int l,r;
    int mark;
    int tlen,llen,rlen;
    int mid(){
        return (l+r)>>1;
    }
    int cal_len(){
        return r-l+1;
    }
    void updata_len(){
        tlen = llen = rlen = ( mark ? 0 : cal_len() );
    }
}t[4*N];

void build(int l ,int r ,int rt)
{
    t[rt].l = l; t[rt].r = r; 
    t[rt].tlen = t[rt].llen = t[rt].rlen = t[rt].cal_len();
    t[rt].mark = 0;
    if(l == r) return ;
    int mid = t[rt].mid();
    build(l , mid , lch(rt));
    build(mid+1 , r , rch(rt));
    return ;
}

int query(int w ,int rt)
{
    if(t[rt].l == t[rt].r && w == 1) //葉子特判
        return t[rt].l;
    if(t[rt].mark != -1) //延遲標記,父親信息傳遞給兒子
    {
        t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark;
        t[rt].mark = -1;
        t[lch(rt)].updata_len(); //傳遞信息后更新孩子的區間覆蓋情況
        t[rch(rt)].updata_len(); //傳遞信息后更新孩子的區間覆蓋情況
    }
    if(t[lch(rt)].tlen >= w) //左孩子的可用區間可以滿足,那么一定在左孩子區間內
        return query(w , lch(rt));
    else if(t[lch(rt)].rlen + t[rch(rt)].llen >= w) //橫跨左右孩子且連續的區間可以滿足,那么可以直接返回下標
        return ( t[lch(rt)].r - t[lch(rt)].rlen + 1 );
    else if(t[rch(rt)].tlen >= w) //右孩子的可用區間可以滿足,那么去右孩子處找
        return query(w , rch(rt));
    else //找不到可用的區間
        return 0;
}

void updata(int l ,int r ,int val ,int rt)
{
    if(t[rt].l == l && t[rt].r == r)
    {
        t[rt].mark = val;
        t[rt].updata_len();
        return ;
    }
    if(t[rt].mark != -1) //延遲標記,父親信息傳遞給兒子
    {
        t[lch(rt)].mark = t[rch(rt)].mark = t[rt].mark;
        t[rt].mark = -1;
        t[lch(rt)].updata_len(); //傳遞信息后更新孩子的區間覆蓋情況
        t[rch(rt)].updata_len(); //傳遞信息后更新孩子的區間覆蓋情況
    }
    int mid = t[rt].mid();
    if(l > mid) //修改的區間在右孩子
        updata(l , r , val , rch(rt));
    else if(r <= mid) //修改的區間在左孩子
        updata(l , r , val , lch(rt));
    else
    {
        updata(l , mid , val , lch(rt));
        updata(mid+1 , r , val , rch(rt));
    }
    int tmp = max(t[lch(rt)].tlen , t[rch(rt)].tlen);
    t[rt].tlen = max(tmp , t[lch(rt)].rlen + t[rch(rt)].llen);
    t[rt].llen = t[lch(rt)].llen;
    t[rt].rlen = t[rch(rt)].rlen;
    if(t[lch(rt)].tlen == t[lch(rt)].cal_len() )
        t[rt].llen += t[rch(rt)].llen;
    if(t[rch(rt)].tlen == t[rch(rt)].cal_len() )
        t[rt].rlen += t[lch(rt)].rlen;
    return ;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while(m--)
    {
        int choose;
        scanf("%d",&choose);
        if(choose == 1) //查詢操作
        {
            int w;
            scanf("%d",&w);
            int index = query(w,1);
            printf("%d\n",index);
            if(index)
                updata(index , index+w-1 , 1 , 1);
        }
        else
        {
            int l,len;
            scanf("%d%d",&l,&len);
            updata(l , l+len-1 , 0 , 1);
        }
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM