czl的知識點整理4 線段樹

2021-08-10 11:54:15 字數 4701 閱讀 5055

首先對於線段樹,其實與其他各種樹都是一樣的,都有著樹形的結構。接下來讓我們考慮一些問題:

~~給出乙個陣列,要求滿足下面的操作:

~~①給定三個值x,y,z,要求吧【x,y】區間的每個數都加上z。

~~②給定兩個值x,y,要求輸出【x,y】區間的最大值(or最小值or和)。

如果我們用暴力做這些問題,那麼對於操作①和②,每次的複雜度為區間的長度。這種方法在資料比較小的時候可以直接用,在資料比較大的情況下就很難滿足時間限制了。這個時候我們就需要用到線段樹。

線段樹,最主要的點就在於它可以滿足對於乙個區間的詢問以及操作,達到o(log)的時間複雜度,能夠更好的滿足資料實時更新的題目要求。

接下來我們就來構建一棵線段樹。首先我們需要考慮線段樹的每個定點上面儲存的值是什麼。由於我們查詢的時候是直接查詢線段樹定點的值,所以我們線段樹的定點所儲存的值就是我們題目中所要求的值(區間最大值or區間最小值or區間和)。然後每次查詢只要從根節點開始查詢,如果當前查詢到的區間在所詢問的區間之內,那麼就返回當前區間的值,否則就繼續向下查詢。

接下來我們分別來介紹線段樹的每個操作(蒟蒻不怎麼會用指標~~見諒):

假定我們有乙個7個數的陣列:1 5 6 9 2 3 4

那麼下面這幅圖就是該陣列的線段樹(區間最小值)表示:

~~①建樹(最基本的~)

首先,由於乙個節點記錄的是乙個區間的最小值,而每個節點的值又會受到下面節點的影響,所以我們可以嘗試著用遞迴的方式來寫。蒟蒻用的是陣列記錄(不怎麼會指標),以零為起點,所以兩個孩子節點就是pos*2+1和pos*2+2。我們定義如下的結構體:

struct node

no[maxn*4];//這裡是很多新手會犯的錯誤之一,因為線段樹是二叉樹,但在最後一層,有很多的子節點是遞迴所要用到的,但是其本身卻沒有存在的意義,所以線段樹的陣列大小一般要開大到原來的2~4倍左右

當遞迴到區間的長度只有1的時候,那麼這個節點的值就是陣列中對應的值,然後回溯回去,比較得出每個非葉子節點的值。

void build(int

pos,int l,int r)

else

}

~~②查詢某個區間的給定值(最大值or最小值or和)

int find(int

pos,int nowl,int nowr,int l,int r)

if(nowl>=l&&nowr<=r)

int mid=(nowl+nowr)>>1;

return min(find(pos

*2+1,nowl,mid,l,r),find(pos

*2+2,mid+1,nowr,l,r));

}

到現在來看,如果線段樹只有這些,那麼還算是比較簡單的了,但是線段樹核心的優化就在於其修改的操作,大大降低了線段樹的總複雜度。

~~③對某一區間的值進行修改

到這一步,我們就要引進乙個新的引數:addmark。把這個引數作為延時標記,具體的作用在下面進行重點講解。

~(1)定義的結構體進行改變:

struct node

no[maxn];

~(2)建樹的時候順便進行初始化,其餘沒變化:

void build(int

pos,int l,int r)

else

}

~(3)新加入乙個操作:push_down,目的在於把延時標記addmark向節點下方傳遞。

void push_down(int now)

}

~(4)查詢的同時進行push_down操作,實時更新陣列資料。(本蒟蒻認為線段樹最為優秀也是最核心的乙個點,最後會進行專門的講解)

int find(int

pos,int nowl,int nowr,int l,int r)

if(nowl>=l&&nowr<=r)

push_down(pos);//查詢時向下傳遞addmark(手動高亮)

int mid=(nowl+nowr)/2;

return min(find(pos

*2+1,nowl,mid,l,r),find(pos

*2+2,mid+1,nowr,l,r));

}

~(5)改變區間的值,同時也向下傳遞markdown(運用遞迴)

void change(int

pos,int nowl,int nowr,int l,int r,int addval)

push_down(pos);//向下傳遞addmark

int mid=(nowl+nowr)/2;

change(pos

*2+1,nowl,mid,l,r,addval);

change(pos

*2+2,mid+1,nowr,l,r,addval);//遞迴進行區間改變值

no[pos].val=min(no[pos

*2+1].val,no[pos

*2+2].val);//回溯後改變當前節點的值

}

放出完整的**:

區間最小值:

struct node

no[maxn];

int a[maxn];

void build(int

pos,int l,int r)

else

}void push_down(int now)

}int find(int

pos,int nowl,int nowr,int l,int r)

if(nowl>=l&&nowr<=r)

push_down(pos);

int mid=(nowl+nowr)/2;

return min(find(pos

*2+1,nowl,mid,l,r),find(pos

*2+2,mid+1,nowr,l,r));

}void change(int

pos,int nowl,int nowr,int l,int r,int addval)

push_down(pos);

int mid=(nowl+nowr)/2;

change(pos

*2+1,nowl,mid,l,r,addval);

change(pos

*2+2,mid+1,nowr,l,r,addval);

no[pos].val=min(no[pos

*2+1].val,no[pos

*2+2].val);

}

區間之和:

typedef long long ll;

struct node

no[maxn*4];

ll a[maxn];

void build(int

pos,int l,int r)

else

}void push_down(int now,int l,int r)

}ll find(int

pos,int nowl,int nowr,int l,int r)

if(nowl>=l&&nowr<=r)

push_down(pos,nowl,nowr);

int mid=(nowl+nowr)/2;

return find(pos

*2+1,nowl,mid,l,r)+find(pos

*2+2,mid+1,nowr,l,r);

}void change(int

pos,int nowl,int nowr,int l,int r,ll addval)

push_down(pos,nowl,nowr);

int mid=(nowl+nowr)/2;

change(pos

*2+1,nowl,mid,l,r,addval);

change(pos

*2+2,mid+1,nowr,l,r,addval);

no[pos].val=no[pos

*2+1].val+no[pos

*2+2].val;

}

最後對push_down這個操作再進行一波解釋。實際上,線段樹在執行了區間改變量值之後,僅僅只有執行區間所對應節點的值發生了改變。舉個例子,就用我們上面所用的陣列,如果計算的是區間的和的話,那麼應該是下面這棵線段樹:

如果我們進行區間改變值得操作,把1~4的每個點加上4。那麼實際上在change操作完成之後,只有區間1~4的這個節點的值變為了47(21+4*4),而這個節點以下的節點的值都沒有進行改變。直到我們進行查詢操作涉及到了1~4這個區間的時候,我們才會將區間1~4的這個節點的addmark向下進行傳遞。所以本蒟蒻認為線段樹最大的優化便是在這個點上,減少了很多不會對查詢有影響的操作,使得演算法更加優秀。

不過在比賽的時候其實不怎麼推薦用線段樹去解題,真的是能不用就不。第一, 線段樹的**量已經有點偏大了,比賽的時候時間本身就比較少。第二,線段樹打完之後仍有許多細節需要調節,也會花費大量的時間。第三,線段樹的陣列要開到原來的2~4倍,所需的空間會比較大。

czl的知識點整理5 單調佇列

既然在模板庫中發了單調佇列的板子,那麼就順便把單調佇列講一講吧。我不會說是在noip前一天攢一攢rp的 單調佇列和單調棧的方法差不多,而且可以算是不會線段樹的oier的福音了。所以單調棧也不會的同學也來學一學吧,反正操作也是差不多的。回歸正題 單調佇列的用途是維護乙個區間的最值的 是不是很像線段樹啊...

czl的知識點整理2 高斯消元

xjoi 1822 civilization 高斯消元其實在小學初中解多元一次方程的時候已經接觸過了。其實,高斯消元就是建立在方程中加減消元和乘除消元之上的。只不過,高斯消元法把這兩種方法應用於矩陣之中,使得高斯消元的複雜度達到o n 相比於真正的去解方程可是要快的多了,想一想你手解100000元一...

Photoshop知識點整理(4)

向量繪圖工具組向量工具 快捷鍵u,切換工具shift u 1.形狀模式 在繪製過程中會自動新建圖層,預設自動填充前景色。2.顏色填充 純色填充 漸變填充 圖案填充。3.圖形描邊 純色填充 漸變填充 圖案填充 描邊大小 描邊選項。4.圖形大小 屬性欄處可以精確調整大小或ctrl t。5.圖形繪製 按住...