線段樹 經典 有趣

2022-09-20 10:06:14 字數 3721 閱讀 2586

一種區間樹,即將除葉子節點的區間拆分成兩個區間,序列以一棵二叉樹的形式呈現。

如圖便是一棵線段樹,我們將通過圖來更深刻的認識線段樹。

1.可以觀察到,這棵線段樹的深度是 \(\log n\)。

2.每個除葉子節點以外節點的左孩子是該節點乘上 \(2\),而右孩子是該節點乘上 \(2\) 加上 \(1\)。

3.可以觀察到此樹是一棵完全二叉樹

眾所周知,性質決定用途,我們可以用線段樹幹什麼呢?

我們令根節點為 \(1\),從根節點一直**,直到分到葉子節點,然後停止**即可。有時候需要在葉子節點傳遞資訊,然後向上傳遞。

時間複雜度:\(o(n\log n)\)

**

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

int mid=(l+r)/2;

build(p*2,l,mid);

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

pushup(p);

}

這裡給出的是去見求和的建樹**。

好像可以很簡單的解決呢

因為只有乙個點,在還沒有到達葉子節點的時候,我們看看它是屬於左子樹還是右子樹,屬於哪邊就往哪邊遞迴,遞迴完之後向上更新即可。

時間複雜度:\(o(\log n)\)

**

void change(int p,int x,int v)

int mid=(l+r)/2;

if(x<=mid) change(p*2,x,v);

else change(p*2+1,x,v);

pushup(p);

}

如何實現呢?

我們結合實際情況來看一下。

任然是這課樹,我們假定我們查詢的區間是 \([3,7]\)。

像這樣,我們仍然從根節點 \(1\) 進行遍歷。

我們發現根節點 \(1\) 沒有完全在這個區間以內,直接返回其值肯定不對。

我們發現其左子樹 \(2\) 有一部分區間在我們查詢的範圍內,我們遞迴到左子樹 \(2\)。

我們發現此時到的節點其右子樹 \(5\) 上有一部分在區間內,但是左子樹 \(4\) 並沒有,所以我們遍歷到右子樹。

此時我們發現區間完全涵蓋,直接返回其值。

由於是遞迴形式,所以我們繼續返回到節點 \(1\) 的右子樹 \(2\)。

操作就跟原來一樣就可以了。

時間複雜度:\(o(\log n)\)

**

int ask(int p,int l,int r)
其實和區間查詢類似,只不過需要引入乙個叫懶標記的東西。

什麼意思呢?

舉乙個生動形象的例子解釋一下:

假設有乙個像線段樹一樣教室:

老師現在要檢查某些同學的作業。

然後前面的同學手裡有答案,老師來了,他早就抄完了答案,老師來了,他就把答案送給了後面同學。

老師沒來,答案就一直放在他手裡。

線段樹區間修改差不多就是這樣,這裡就不細講了。

時間複雜度:\(o(\log n)\)

**

void change(int u,int l,int r,int d)

pushdown(u);

int mid=(tr[u].l+tr[u].r)/2;

if(l<=mid) change(u*2,l,r,d);

if(r>mid) change(u*2+1,l,r,d);

pushup(u);

}

權值線段樹可以解決大部分關於數值上的問題,大致就是以所有的權值開一棵線段樹,在 \(\log n\) 的效率下解決大部分問題。

但是這樣它的缺陷就比較明顯,不適用於那些權值比較大的操作。不然在空間上劣勢十分明顯,但不過由於操作個數有限,我們往往可以通過離散化來進行優化。

大致操作與線段樹是類似的,可以通過一些操作使得其發揮得更加淋漓盡致。

它可以解決一些平衡樹問題。

#includeusing namespace std;

const int n=1e5+5;

int m,f[n],cnt;

mapq;

struct nodet[4*n];

struct qd[n];

void pushup(int p)

void build(int p,int l,int r));

if(l==r) return ;

int mid=(l+r)/2;

build(p*2,l,mid);

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

}void change(int p,int x,int k)

int mid=(t[p].l+t[p].r)/2;

if(x<=mid) change(p*2,x,k);

else change(p*2+1,x,k);

pushup(p);

}int get_rank(int p,int x)

int get_val(int p,int x)

int get_pre(int p,int x)

int mid=(t[p].l+t[p].r)/2;

if(mid>=x) return get_pre(p*2,x);

int t=get_pre(p*2+1,x);

if(t) return t;

return get_pre(p*2,x);

}int get_next(int p,int x)

int mid=(t[p].l+t[p].r)/2;

if(mid<=x) return get_next(p*2+1,x);

int t=get_next(p*2,x);

if(t) return t;

return get_next(p*2+1,x);

}int main()

sort(f+1,f+1+cnt);

for(int i=1;i<=cnt;i++) q[f[i]]=i;

for(int i=1;i<=m;i++)

return 0;

}

動態開點比較好用,它避免了一種尷尬的局面,那就是權值線段樹需要進行離散化,肯定很多的。。。

咕咕咕

有趣的有趣的家庭菜園 線段樹優化dp

職業經營家庭菜園的joi君每年在自家的田地中種植一種叫做ioi草的植物。ioi草的種子在冬天被播下,春天會發芽並生長至乙個固定的高度。到了秋天,一些ioi草會結出美麗的果實,並被收穫,其他的ioi草則會在冬天枯萎。joi君的田地沿東西方向被劃分為n個區域,從西側開始的第i個區域中種植著ioi草i。在...

線段樹經典題目 矩形面積

有n 100000 個長方形,並且它的邊長平行於x軸或y軸 範圍50000 求這n個長方形覆蓋的面積,重疊部分的面積只算一次。在平面上的一些統計的題目,往往是用線段樹,把x軸作為掃瞄線,然後把y軸對映到線段樹上。這題也是如此 若座標範圍太大,可以將其離散化,這題不需要。如圖,有兩個矩形。豎細線作為掃...

FZU 2176 經典線段樹

problem 2176 easy problem accept 38 submit 119 time limit 2000 msec memory limit 32768 kb problem description 給定一棵n個節點以1為根的樹,初始每個節點的值為0,現在我們要在樹上進行一些操作...