線段樹區間合併

2021-08-23 12:15:44 字數 4401 閱讀 7237

線段樹區間合併主要解決一段連續區間修改,查詢問題。

線段樹是樹形結構,為解決相鄰區間更新,修改問題,我們必須在原本須要維護的區間內,連續段最大值的基礎上加上,從左,右兩邊起,最大的連續區間長度。

例題:酒店:

此題開始時,所有的房間都是空房間,有兩個操作:

1、查詢長度為x的連續房間,並將房間全部填滿,輸出左端點值。

2、表示退房,將x,到x+y-1的房間退房。

我們來看線段樹,我們知道線段樹要將區間分成好多塊,之後對每一塊進行處理。對於兩塊深度相同的區間,如果只是單純的維護乙個區間最大值,很明顯。對於兩個區間共同的祖宗區間,你無法判斷,祖宗區間的最大值,是它左兒子的最大值還是右兒子的最大值,還是這個最大的連續區間在兩個區間之間。因此引入兩個新的值,這個區間從最左段起最大的連續區間,還有從這個區間最右端起最大的連續區間,這樣我們就可以判斷祖宗節點最大的連續區間到底是多大。

向上更新:

對於我們維護的三個值,左最大跟右最大的更新幾乎是一樣的:

(圖比較醜,emm,手殘多見諒。)

我們看到祖宗節點的左最大為1,就是左兒子的左最大,右最大為4,我們單看右兒子最大只為3,但是左兒子還有乙個點與它們相鄰,因此當右兒子右最大等於它所包含的區間長度時,那麼我們就可以將左兒子右最大也更新到祖宗節點中,具體見**。

向下更新,對於lazy節點我們只需要讓他記錄是開房,還是退房,記錄完畢向下更新,就將區間全部清空,或者全部住房即可。

對於查詢,我們可以看到,需要查詢長度為x的區間,輸出的是最左端的房間,也就是說有多個空房時,我們只要輸出最左端的那乙個,那麼我們的查詢只要從最左端找起,如果有就輸出沒有就繼續查詢中間,最後查詢最右端。,找到最左端值後,我們再根據區間長度呼叫更新函式,區間更新即可。

**:

#include #include #include #define ll long long

const int m=1e5+1000;

using namespace std;

//對於入住情況,先查詢滿足情況的左端點。然後根據左端點,求出更新區間,對整個區間進行更新

struct node

tr[m*4];

void build(int i,int l,int r)

int mid=(l+r)/2;

build(i*2,l,mid);

build(i*2+1,mid+1,r);

}void pushup(int i,int l,int r)

else

tr[i].lmax=tr[i*2].lmax;

if(tr[i*2+1].sum==(r-mid))

else

tr[i].rmax=tr[i*2+1].rmax;

tr[i].sum=max(tr[i*2].rmax+tr[i*2+1].lmax,max(tr[i*2].sum,tr[i*2+1].sum));

}void pushdown(int i,int l,int r)

if(tr[i].lazy==2)

tr[i].lazy=0;

}ll query(int i,int l,int r,int x)

void update(int i,int l,int r,int x,int y,int t)

else

tr[i].lazy=t;

return;

}int mid=(l+r)/2;

if(x<=mid) update(i*2,l,mid,x,y,t);

if(y>mid) update(i*2+1,mid+1,r,x,y,t);

pushup(i,l,r);

}int main()

else

}else

}return 0;

}

這個題除了酒店的操作之外,需要釋放x位置所在的區間,得到從左數第x個區間開始的位置。

這題需要用乙個vector來維護,我們儲存的空間,這樣方便查詢第x個位置所在的空間,跟從左數第x個區間開始位置。因為我們輸出從左數第x個空間開始的位置,因此我們的vector要有序,有序插入,怎麼才能縮短時間複雜度?二分!下面看**。

#include #include #include #include #define ll long long

const int m=1e5+1000;

using namespace std;

//對於入住情況,先查詢滿足情況的左端點。然後根據左端點,求出更新區間,對整個區間進行更新

struct node

tr[m*4];

void build(int i,int l,int r)

int mid=(l+r)/2;

build(i*2,l,mid);

build(i*2+1,mid+1,r);

}void pushup(int i,int l,int r)

else

tr[i].lmax=tr[i*2].lmax;

if(tr[i*2+1].sum==(r-mid))

else

tr[i].rmax=tr[i*2+1].rmax;

tr[i].sum=max(tr[i*2].rmax+tr[i*2+1].lmax,max(tr[i*2].sum,tr[i*2+1].sum));

}void pushdown(int i,int l,int r)

if(tr[i].lazy==2)

tr[i].lazy=0;

}ll query(int i,int l,int r,int x)

void update(int i,int l,int r,int x,int y,int t)

else

tr[i].lazy=t;

return;

}if(tr[i].lazy!=0)

pushdown(i,l,r);

int mid=(l+r)/2;

if(x<=mid) update(i*2,l,mid,x,y,t);

if(y>mid) update(i*2+1,mid+1,r,x,y,t);

pushup(i,l,r);

}typedef pairpil;

vectorlist;

int find(int k)

else

}return ans;

}int main()

else

}else if(o[0]=='r')

else if(o[0]=='f')

else

printf("reject free\n");

}else

else

printf("reject get\n");}}

printf("\n");

}return 0;

}

hdu 1540 tunnel wa***re

這個題查詢跟上面的題有一定的相似,查詢x所在區間最左端值,此題可用線段樹解決,判斷x在哪個區間中:

#include #include #include #include #include using namespace std;

const int m= 50000+10;

struct

tr[m*4];

int r[m],cnt;

void pushup(int i)

else

tr[i].lmax=tr[i*2].lmax;

if(tr[i*2+1].rmax==tr[i*2+1].len)

else

tr[i].rmax=tr[i*2+1].rmax;

tr[i].maxn=max(max(tr[i*2].maxn,tr[i*2+1].maxn),tr[i*2].rmax+tr[i*2+1].lmax);

}void build(int i,int l,int r)

void update(int i,int l,int r,int x,int o)

else

tr[i].lmax=tr[i].rmax=tr[i].maxn=0;

return;

}int mid=(r+l)/2;

if(x<=mid)update(i*2,l,mid,x,o);

else update(i*2+1,mid+1,r,x,o);

pushup(i);

}int query(int i,int l,int r,int x)

int mid=(r+l)/2;

if(x<=mid)

}else

else

}}int main()

}else}}

return 0;

}

線段樹 區間合併

hdu 1540 tunnel wa re 詳細見 include include include include include define max 50010 define lson l,m,k 1 define rson m 1,r,k 1 1 using namespace std typ...

線段樹 區間合併

1 poj 3667 題意 支援兩種操作 1 如果有連續長度大於d的房間,則輸出最左的區間值,否則輸出0 2 將ql,qr區間的房間標記為 未入住 思路 節點維護三個域,該節點的最大房間數,該節點左端點起的最大房間數,該房間右端點起的最大連續房間數,用線段樹進行更新和查詢 include inclu...

線段樹 區間合併

線段樹的區間合併,即尋找 詢問區間中滿足條件的連續最長區間。而乙個區間連續的最長區間有兩種情況 1 此連續最長區間全在左子樹或全在右子樹,則sum t max sum t 1 sum t 1 1 2 一部分在左子樹,一部分在右子樹,則sum t suml t 1 1 sum t 1 因此,我們需要記...