線段樹區間合併主要解決一段連續區間修改,查詢問題。
線段樹是樹形結構,為解決相鄰區間更新,修改問題,我們必須在原本須要維護的區間內,連續段最大值的基礎上加上,從左,右兩邊起,最大的連續區間長度。
例題:酒店:
此題開始時,所有的房間都是空房間,有兩個操作:
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 因此,我們需要記...