1. 程式人生 > 其它 >GPS北斗衛星校時伺服器的關鍵技術和特點

GPS北斗衛星校時伺服器的關鍵技術和特點

前置知識

二叉樹

定義

基本操作複雜度: $ O(logn) $

線段樹是一棵二叉樹,每個節點表示序列上的一段區間,其中根節點表示區間[1,n]。

從根節點開始,只要區間長度不為1,就將區間劃分為兩半,並分給兩個子節點。

若當前節點表示區間[l,r],

當l≠r時,左孩子表示[l,(l+r)/2],右孩子表示[(l+r)/2+1,r];

當l=r時,該節點為葉子節點。

基本性質

性質1

線段樹的總節點數為2n-1。

性質2

線段樹的層數約為 $ log_{ 2 } n $ ,即每個葉節點到根節點的距離約為 \(log_{2}n\)

性質3

任何區間都可以用不超過 \(2log_{2}n\)

個節點組合而成。

編號

線段樹的編號方式通常有兩種:即2i和2i+1作i的孩子或按構建順序編號。

儲存

線段樹的儲存也有兩種方式:多個數組或struct結構體。

以struct結構體陣列為例:

(lc,rc分別為左右孩子的編號,sum為該區間的總和。

陣列大小為序列長度兩倍。

至於區間的左右端點[l,r],可以儲存,也可以遞迴時計算。

若需要節省空間,則不儲存。)

構建

構建過程遞迴實現,

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

(u為當前節點編號,l,r為區間端點)

從根節點開始構建,遞迴時計算並代入l,r,若l=r,該點為葉子節點,令sum=序列原值,否則,遞迴構建左右孩子,之後令sum=sum左孩子+sum右孩子。

在主程式中:

t=1;

build(1,1,n);

即可。

應用

單點修改

修改過程遞迴實現,

void update(int u,int l,int r,int x,int w)

(u當前節點編號,l和r為區間端點,x為要修改的位置,w為變化量)

從根節點開始修改,若當前點為葉子節點,使sum+=w並返回
否則判斷x在左孩子還是右孩子,遞迴進行修改,之後令sum=左孩子sum+右孩子sum。

在主程式中呼叫:

update(1,1,n,x,y);

區間和查詢

查詢過程遞迴實現,

void query(int u,int l,int r,int ll,int rr)

(u為當前節點,l和r是區間端點,ll和rr分別為所查詢區間的左右端點)

需要用外層定義變數ans儲存答案。

從根節點開始,首先判斷查詢區間與節點區間是否完全重合,若重合,ans+=sum並返回,否則判斷查詢區間是否完全在左孩子或者右孩子,若是則遞迴查詢。否則將查詢區間拆分,兩側分別遞迴查詢。

在主程式中:

ans=0;

query(1,1,n,x,y);

維護區間最大值/最小值

區間修改

我們線上段樹的每個節點上再儲存一個資訊,稱為“標記”,記為tag,表示該節點所包含的每個位置,都要再加上tag。

首先,像區間詢問一樣將區間對應到儘量少的節點上,

令它們

	sum+=(r-l+1)*w;//增加的區間和
	tag+=w;//增加標記 

然後將sum資訊向上pushup。

區間修改後的單點/區間查詢

查詢與之前類似,

查詢過程遞迴實現,

void query(int u,int l,int r,int ll,int rr,int w)

但由於標記的出現,多了“下傳標記”一步。

下傳標記時,孩子的標記要疊加。

類似於pushup,標記下傳的過程寫入一個

void pushdown(int u,int l,int r)

一個節點上的tag一定已經計入自身的sum。

在修改和查詢時,只要需要進入子節點,就一定要pushdown。

注意

當維護資訊較多時,為簡化程式碼,通常將類似於”a[u].sum=a[a[u].lc].sum+a[a[u].rc].sum”的上傳資訊的步驟寫入void pushup(int u)並呼叫pushup(u);

並非原創,僅是整理,請見諒