線段樹 操作格仔(藍橋杯試題集)

2021-07-24 20:08:19 字數 3439 閱讀 2794

題目描述:

有n個格仔,從左到右放成一排,編號為1-n。

共有m次操作,有3種操作型別:

1.修改乙個格仔的權值,

2.求連續一段格仔權值和,

3.求連續一段格仔的最大值。

對於每個2、3操作輸出你所求出的結果。

輸入格式

第一行2個整數n,m。

接下來一行n個整數表示n個格仔的初始權值。

接下來m行,每行3個整數p,x,y,p表示操作型別,p=1時表示修改格仔x的權值為y,p=2時表示求區間[x,y]內格仔權值和,p=3時表示求區間[x,y]內格仔最大的權值。

輸出格式

有若干行,行數等於p=2或3的操作總數。

每行1個整數,對應了每個p=2或3操作的結果。

樣例輸入

4 31 2 3 4

2 1 3

1 4 3

3 1 4

樣例輸出63

資料規模與約定

對於20%的資料n <= 100,m <= 200。

對於50%的資料n <= 5000,m <= 5000。

對於100%的資料1 <= n <= 100000,m <= 100000,0 <= 格仔權值 <= 10000。

解題思路:

乍一看,不怎麼難。然而寫一波迴圈超時之後。

本題求和、最大值操作顯然不能迴圈計算求取,那麼就要想辦法再此之前儲存這些值。那麼問題就來了,我們要怎樣儲存呢?

先介紹一下線段樹:

對於線段樹中的每乙個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目為n,即整個線段區間的長度。

線段樹至少支援下列操作:

insert(t,x):將包含在區間 int 的元素 x 插入到樹t中;

delete(t,x):從線段樹 t 中刪除元素 x;

search(t,x):返回乙個指向樹 t 中元素 x 的指標。

好,我們把其代入本題:

首先說一下建樹:void build(int i,int l,int r)//引入節點標和左右標 

假設輸入n=8,那我們需要構造的線段即[1,8],即我們的建樹操作為:build(1,1,8);  //首先從節點1開始,左極限為1,右極限為8

我們的build函式有以下幾個操作:

①如果l==r,也就是說,當前引入點左右相等,那麼就是乙個單獨的數,我們就不再需要向下遞迴

②如果l不等於r,我們需要創造他的2個子節點:

build(2*i,l,(l+r)/2);       //我們知道二叉樹子節點的編號分別為2i,2i+1

build(2*i+1,(l+r)/2+1,r);   //我們遞迴構造子節點時,對於奇數線段比如說[1-3],我們有兩種選擇,[1-2][3]或者[1-2][2-3] 這裡我們採取前者。

③遞迴回歸時將其有用的權值返回父節點,父節點針對於子節點的權值構造自身權值

比如這道題的sum與max:

/* 回歸時得到當前i節點的max資訊 */    

linetree[i].node_max=fmax(linetree[2*i].node_max,linetree[2*i+1].node_max);

/* 回歸時得到當前i節點的sum資訊 */  

linetree[i].node_sum=linetree[2*i].node_sum+linetree[2*i+1].node_sum;  

接下來我們講更新:

①當葉子節點更新的時候,所有其父路徑直至根節點走要進行一次更新操作,即時間複雜度為o(logn)

②找葉子節點也需要花費o(logn)從根節點遞迴查詢至葉子節點

最後,我們講查詢:

①查詢分為點查詢,線段查詢(即線段左右點是否相等)

②線段查詢,該線段可能在樹中完全包含,也可能是半包含。例如構造線段[1,2,3,4] 即:

[1,2,3,4]

[1,2]        [3,4]

[1]    [2]    [3]    [4]

如果我們想搜尋[1,3]線段,那麼可以看到,該樹是不存在這樣的線段的,那麼我們就需要通過[1,2]和[3]來計算[1,2,3]所返回的權值

下面貼出完整**:

#include#define n 100010   

struct linetree

linetree[n*4]; //不是2n-1!!!不是2n-1!!!不是2n-1!!!

int box[n]; // 盒子

int _max;

int _sum;

int fmax(int a,int b)

void build(int i,int l,int r)//引入節點標和左右標 }

void updata(int i,int x,int y)/*單節點更新 y替換x */

else

i/=2;}}

else //遞迴遍歷

}int readmax(int i,int x,int y) //區間最大值查詢

i++;

if(y>=linetree[i].node_lpoint) //左區間

}}int readsum(int i,int x,int y) //區間和 遍歷同上

i++;

if(y>=linetree[i].node_lpoint)

}}

int main()}}

}

注意!!這個問題也是困擾了我很久的乙個問題,我們申請線段樹時候,其空間到底是多少? 

對於線段樹來說,n也就是單獨的樹最終會成為其葉子節點,二叉樹所需要的節點是2n-1,那麼我們能否認為線段樹所需要的空間即2n-1?

然而實時證明,這是錯誤的。

我們假設構造(1,11)這個線段並對其節點進行標註

(1,11)【節點1】

(1,5)  【節點2】(6,11)【節點3】

(1,3)  【節點4】(4,5)  【節點5】 (6,8)【節點6】(9,11)【節點7】

(1,2)  【節點8】(3)       【節點9】(4)      【節點10】(5)     【節點11】(6,7)【節點12】(8)【節點13】(9,10)【節點14】(11)【節點15】

我們看,節點14也就是(9,10)還需要遞迴擴充套件子樹

他的子樹應該是2n,2n+1,也就是28,29

咦?11個數不應該是21個節點嗎?   其實是這樣的,在之前的葉子節點(比如8,9)雖然沒有子節點了,但是他仍然占有16,17,18,19的位置

為了滿足我們的查詢規律。所以,我們申請資源的時候不應該設為2n-1

藍橋杯 操作格仔 線段樹

題目 有n個格仔,從左到右放成一排,編號為1 n。共有m次操作,有3種操作型別 1.修改乙個格仔的權值,2.求連續一段格仔權值和,3.求連續一段格仔的最大值。對於每個2 3操作輸出你所求出的結果。輸入格式 第一行2個整數n,m。接下來一行n個整數表示n個格仔的初始權值。接下來m行,每行3個整數p,x...

藍橋杯 操作格仔 線段樹

剛學習了線段樹,解決區間問題確實是不錯的利器,線段樹實際上就是一棵平衡二叉樹,對於任何操作都能在o long2n 的時間內完成,相比對普通陣列o n 的時間複雜度,有不錯的效率,下面以藍橋網上乙個題操練一下吧。問題描述 有n個格仔,從左到右放成一排,編號為1 n。共有m次操作,有3種操作型別 1.修...

藍橋杯演算法訓練 格仔操作 線段樹

這題設計最基本的線段樹應用,同時考察區間和與區間最值,我採用乙個造樹函式,乙個更新函式和兩個查詢查詢函式,兩個查詢函式分別返回區間和與區間最大值。問題描述 有n個格仔,從左到右放成一排,編號為1 n。共有m次操作,有3種操作型別 1.修改乙個格仔的權值,2.求連續一段格仔權值和,3.求連續一段格仔的...