基本線段樹模板(建樹、點/區間修改、查詢)


線段樹主要用於區間記錄信息(如區間和、最大最小值等),首先是建樹:

 

這里以求和為例:

 1 const int MAXM=50000;          //定義 MAXM 為線段最大長度  2 
 3 int a[MAXM+5],st[(MAXM<<2)+5];    // a 數組為 main 函數中讀入的內容,st 數組為需要查詢的數的信息(如和、最值等),樹的空間大小為線段最大長度的四倍  4 
 5 void build(int o,int l,int r){    //傳入的參數為 o:當前需要建立的結點;l:當前需要建立的左端點;r:當前需要建立的右端點  6     if(l==r)st[o]=a[l];      //當左端點等於右端點即建立葉子結點時,直接給數組信息賦值  7     else{  8         int m=l+((r-l)>>1);      // m 為中間點,左兒子結點為 [l,m] ,右兒子結點為 [m+1,r];  9         build(o<<1,l,m);        //構建左兒子結點 10         build((o<<1)|1,m+1,r);     //構建右兒子結點 11         st[o]=st[o<<1]+st[(o<<1)|1];  //遞歸返回時用兒子結點更新父節點,此處可進行更新最大值、最小值、區間和等操作 12  } 13 } 14 
15 {                       //在 main 函數中的語句 16         build(1,1,n); 17 }

 

然后是比較簡單的單點修改以及區間查詢操作:

單點修改:

 1 void update(int o,int l,int r,int ind,int ans){  //o、l、r為當前更新到的結點、左右端點,ind為需要修改的葉子結點左端點,ans為需要修改成的值;  2     if(l==r){                      //若當前更新點的左右端點相等即到葉子結點時,直接更新信息並返回  3         st[o]=ans;  4         return;  5  }  6     int m=l+((r-l)>>1);  7     if(ind<=m){                      //若需要更新的葉子結點在當前結點的左兒子結點的范圍內,則遞歸更新左兒子結點,否則更新右兒子結點  8         update(o<<1,l,m,ind,ans);  9  } 10     else{ 11         update((o<<1)|1,m+1,r,ind,ans); 12  } 13     st[o]=max(st[o<<1],st[(o<<1)|1]);//遞歸回之后用兒子結點更新父節點(此處是區間最大值) 14 } 15 
16 {                               //在main函數中的語句 17         update(1,1,n,ind,ans); 18 }    

對應單點修改的區間查詢:

 1 int query(int o,int l,int r,int ql,int qr){      //ql、qr為需要查詢的區間左右端點  2     if(ql>r||qr<l) return -1;              //若當前結點和需要查找的區間不相交,則返回一個對於區間查詢無關的值(如求和時返回0,求最大值時返回-1等)  3     if(ql<=l&&qr>=r) return st[o];        //若當前結點的區間被需要查詢的區間覆蓋,則返回當前結點的信息  4     int m=l+((r-l)>>1);  5     int p1=query(o<<1,l,m,ql,qr),p2=query((o<<1)|1,m+1,r,ql,qr);  //p1為查詢左兒子結點得到的信息,p2為查詢右兒子結點得到的信息  6     return max(p1,p2);    //綜合兩個兒子結點的信息並返回  7 }  8 
 9 {    //main函數中的語句 10         printf("%d\n",query(1,1,n,a,b)); 11 }

 

然后是線段數的區間修改以及相應的查詢:

區間修改用到了lazy的思想,即當一個區間需要更新時,只遞歸更新到那一層結點,並將其下層結點所需要更新的信息保存在數組中,然后返回,只有當下次遍歷到那個結點(更新過程中或查詢過程中),才將那個結點的修改信息傳遞下去,這樣就避免了區間修改的每個值的修改

區間修改(包括區間加值和區間賦值)及相應查詢:

區間加值:

 1 void pushup(int o){          //pushup函數,該函數本身是將當前結點用左右子節點的信息更新,此處求區間和,用於update中將結點信息傳遞完返回后更新父節點
 2     st[o]=st[o<<1]+st[o<<1|1];  3 }  4   
 5 void pushdown(int o,int l,int r){  //pushdown函數,將o結點的信息傳遞到左右子節點上
 6     if(add[o]){             //當父節點有更新信息時才向下傳遞信息
 7         add[o<<1]+=add[o];      //左右兒子結點均加上父節點的更新值
 8         add[o<<1|1]+=add[o];  9         int m=l+((r-l)>>1); 10         st[o<<1]+=add[o]*(m-l+1);  //左右兒子結點均按照需要加的值總和更新結點信息
11         st[o<<1|1]+=add[o]*(r-m); 12         add[o]=0;                //信息傳遞完之后就可以將父節點的更新信息刪除
13  } 14 } 15  
16 void update(int o,int l,int r,int ql,int qr,int addv){  //ql、qr為需要更新的區間左右端點,addv為需要增加的值
17     if(ql<=l&&qr>=r){                      //與單點更新一樣,當當前結點被需要更新的區間覆蓋時
18         add[o]+=addv;                      //更新該結點的所需更新信息
19         st[o]+=addv*(r-l+1);                //更新該結點信息
20         return;                    //根據lazy思想,由於不需要遍歷到下層結點,因此不需要繼續向下更新,直接返回
21  } 22     
23     pushdown(o,l,r);                  //將當前結點的所需更新信息傳遞到下一層(其左右兒子結點)
24     int m=l+((r-l)>>1); 25     if(ql<=m)update(o<<1,l,m,ql,qr,addv);     //當需更新區間在當前結點的左兒子結點內,則更新左兒子結點
26     if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,addv);   //當需更新區間在當前結點的右兒子結點內,則更新右兒子結點
27     pushup(o);                  //遞歸回上層時一步一步更新回父節點
28 } 29 
30 ll query(int o,int l,int r,int ql,int qr){    //ql、qr為需要查詢的區間
31     if(ql<=l&&qr>=r) return st[o];      //若當前結點覆蓋區間即為需要查詢的區間,則直接返回當前結點的信息
32     pushdown(o,l,r);                  //將當前結點的更新信息傳遞給其左右子節點
33     int m=l+((r-l)>>1); 34     ll ans=0;                      //所需查詢的結果
35     if(ql<=m)ans+=query(o<<1,l,m,ql,qr);     //若所需查詢的區間與當前結點的左子節點有交集,則結果加上查詢其左子節點的結果
36     if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr); //若所需查詢的區間與當前結點的右子節點有交集,則結果加上查詢其右子節點的結果
37    return ans; 38 }

 

區間改值(其實只有pushdow函數和update中修改部分與區間加值不同):

 

 1  void pushup(int o){  2      st[o]=st[o<<1]+st[o<<1|1];  3  }  4  
 5  void pushdown(int o,int l,int r){  //pushdown和區間加值不同,改值時修改結點信息只需要對修改后的信息求和即可,不用加上原信息
 6      if(change[o]){  7          int c=change[o];  8          change[o<<1]=c;  9          change[o<<1|1]=c; 10          int m=l+((r-l)>>1); 11          st[o<<1]=(m-l+1)*c; 12          st[o<<1|1]=(r-m)*c; 13          change[o]=0; 14  } 15  } 16  
17  void update(int o,int l,int r,int ql,int qr,int c){ 18      if(ql<=l&&qr>=r){         //同樣更新結點信息和區間加值不同
19          change[o]=c; 20          st[o]=(r-l+1)*c; 21          return; 22  } 23      
24 pushdown(o,l,r); 25 int m=l+((r-l)>>1); 26 if(ql<=m)update(o<<1,l,m,ql,qr,c); 27 if(qr>=m+1)update(o<<1|1,m+1,r,ql,qr,c); 28 pushup(o); 29 } 30 31 int query(int o,int l,int r,int ql,int qr){ 32 if(ql<=l&&qr>=r) return st[o]; 33 pushdown(o,l,r); 34 int m=l+((r-l)>>1); 35 int ans=0; 36 if(ql<=m)ans+=query(o<<1,l,m,ql,qr); 37 if(qr>=m+1)ans+=query(o<<1|1,m+1,r,ql,qr); 38 return ans; 39 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM