題目描述:
有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.求連續一段格仔的...