日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

线段树(自看)

發(fā)布時(shí)間:2023/11/27 生活经验 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线段树(自看) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

線段樹(shù)的入門(mén)級(jí) 總結(jié)


??????線段樹(shù)是一種二叉搜索樹(shù),與區(qū)間樹(shù)相似,它將一個(gè)區(qū)間劃分成一些單元區(qū)間,每個(gè)單元區(qū)間對(duì)應(yīng)線段樹(shù)中的一個(gè)葉結(jié)點(diǎn)。
??????對(duì)于線段樹(shù)中的每一個(gè)非葉子節(jié)點(diǎn)[a,b],它的左兒子表示的區(qū)間為[a,(a+b)/2],右兒子表示的區(qū)間為[(a+b)/2+1,b]。因此線段樹(shù)是平衡二叉樹(shù),最后的子節(jié)點(diǎn)數(shù)目為N,即整個(gè)線段區(qū)間的長(zhǎng)度。
??????使用線段樹(shù)可以快速的查找某一個(gè)節(jié)點(diǎn)在若干條線段中出現(xiàn)的次數(shù),時(shí)間復(fù)雜度為O(logN)。而未優(yōu)化的空間復(fù)雜度為2N,因此有時(shí)需要離散化讓空間壓縮。

----來(lái)自百度百科 【以下以 求區(qū)間最大值為例】
先看聲明:

[cpp]?view plaincopy
  1. #include?<stdio.h>??
  2. #include?<math.h>??
  3. const?int?MAXNODE?=?2097152;??
  4. const?int?MAX?=?1000003;??
  5. struct?NODE{??
  6. ????int?value;????????//?結(jié)點(diǎn)對(duì)應(yīng)區(qū)間的權(quán)值??
  7. ????int?left,right;???//?區(qū)間?[left,right]??
  8. }node[MAXNODE];??
  9. int?father[MAX];?????//?每個(gè)點(diǎn)(當(dāng)區(qū)間長(zhǎng)度為0時(shí),對(duì)應(yīng)一個(gè)點(diǎn))對(duì)應(yīng)的結(jié)構(gòu)體數(shù)組下標(biāo)??


【創(chuàng)建線段樹(shù)(初始化)】:

?????? 由于線段樹(shù)是用二叉樹(shù)結(jié)構(gòu)儲(chǔ)存的,而且是近乎完全二叉樹(shù)的,所以在這里我使用了數(shù)組來(lái)代替鏈表上圖中區(qū)間上面的紅色數(shù)字表示了結(jié)構(gòu)體數(shù)組中對(duì)應(yīng)的下標(biāo)。

在完全二叉樹(shù)中假如一個(gè)結(jié)點(diǎn)的序號(hào)(數(shù)組下標(biāo))為?I?,那么 (二叉樹(shù)基本關(guān)系)

I 的父親為 I/2,

I 的另一個(gè)兄弟為 I/2*2 或 I/2*2+1

I 的兩個(gè)孩子為 I*2 (左)?? I*2+1(右)

有了這樣的關(guān)系之后,我們便能很方便的寫(xiě)出創(chuàng)建線段樹(shù)的代碼了。


[cpp]?view plaincopy
  1. void?BuildTree(int?i,int?left,int?right){?//?為區(qū)間[left,right]建立一個(gè)以i為祖先的線段樹(shù),i為數(shù)組下標(biāo),我稱(chēng)作結(jié)點(diǎn)序號(hào)??
  2. ????node[i].left?=?left;????//?寫(xiě)入第i個(gè)結(jié)點(diǎn)中的?左區(qū)間??
  3. ????node[i].right?=?right;??//?寫(xiě)入第i個(gè)結(jié)點(diǎn)中的?右區(qū)間??
  4. ????node[i].value?=?0;??????//?每個(gè)區(qū)間初始化為?0??
  5. ????if?(left?==?right){?//?當(dāng)區(qū)間長(zhǎng)度為?0?時(shí),結(jié)束遞歸??
  6. ????????father[left]?=?i;?//?能知道某個(gè)點(diǎn)對(duì)應(yīng)的序號(hào),為了更新的時(shí)候從下往上一直到頂??
  7. ????????return;??
  8. ????}??
  9. ????//?該結(jié)點(diǎn)往?左孩子的方向?繼續(xù)建立線段樹(shù),線段的劃分是二分思想,如果寫(xiě)過(guò)二分查找的話這里很容易接受??
  10. ????//?這里將?區(qū)間[left,right]?一分為二了??
  11. ????BuildTree(i<<1,?left,?(int)floor(?(right+left)?/?2.0));??
  12. ????//?該結(jié)點(diǎn)往?右孩子的方向?繼續(xù)建立線段樹(shù)??
  13. ????BuildTree((i<<1)?+?1,?(int)floor(?(right+left)?/?2.0)?+?1,?right);??
  14. }??


【單點(diǎn)更新線段樹(shù)】:

?????? 由于我事先用 father[ ] 數(shù)組保存過(guò) 每單個(gè)結(jié)點(diǎn) 對(duì)應(yīng)的下標(biāo)了,因此我只需要知道第幾個(gè)點(diǎn),就能知道這個(gè)點(diǎn)在結(jié)構(gòu)體中的位置(即下標(biāo))了,這樣的話,根據(jù)之前已知的基本關(guān)系,就只需要直接一路更新上去即可。


[cpp]?view plaincopy
  1. void?UpdataTree(int?ri){?//?從下往上更新(注:這個(gè)點(diǎn)本身已經(jīng)在函數(shù)外更新過(guò)了)??
  2. ??
  3. ????if?(ri?==?1)return;?//?向上已經(jīng)找到了祖先(整個(gè)線段樹(shù)的祖先結(jié)點(diǎn)?對(duì)應(yīng)的下標(biāo)為1)??
  4. ????int?fi?=?ri?/?2;????????//?ri?的父結(jié)點(diǎn)??
  5. ????int?a?=?node[fi<<1].value;?//?該父結(jié)點(diǎn)的兩個(gè)孩子結(jié)點(diǎn)(左)??
  6. ????int?b?=?node[(fi<<1)+1].value;?//?右??
  7. ????node[fi].value?=?(a?>?b)?(a):(b);????//?更新這個(gè)父結(jié)點(diǎn)(從兩個(gè)孩子結(jié)點(diǎn)中挑個(gè)大的)??
  8. ????UpdataTree(ri/2);???????//?遞歸更新,由父結(jié)點(diǎn)往上找??
  9. }??



【查詢(xún)區(qū)間最大值】:
?????? 將一段區(qū)間按照建立的線段樹(shù)從上往下一直拆開(kāi),直到存在有完全重合的區(qū)間停止。對(duì)照?qǐng)D例建立的樹(shù),假如查詢(xún)區(qū)間為 [2,5]?

紅色的區(qū)間為完全重合的區(qū)間,因?yàn)樵谶@個(gè)具體問(wèn)題中我們只需要比較這 三個(gè)區(qū)間的值 找出 最大值 即可。


[cpp]?view plaincopy
  1. int?Max?=?-1<<20;??
  2. void?Query(int?i,int?l,int?r){?//?i為區(qū)間的序號(hào)(對(duì)應(yīng)的區(qū)間是最大范圍的那個(gè)區(qū)間,也是第一個(gè)圖最頂端的區(qū)間,一般初始是?1?啦)??
  3. ????if?(node[i].left?==?l?&&?node[i].right?==?r){?//?找到了一個(gè)完全重合的區(qū)間??
  4. ????????Max?=?(Max?<?node[i].value)?node[i].value:(Max);??
  5. ????????return?;??
  6. ????}??
  7. ????i?=?i?<<?1;?//?get?the?left?child?of?the?tree?node??
  8. ????if?(l?<=?node[i].right){?//?左區(qū)間有涉及??
  9. ????????if?(r?<=?node[i].right)?//?全包含于左區(qū)間,則查詢(xún)區(qū)間形態(tài)不變??
  10. ????????????Query(i,?l,?r);??
  11. ????????else?//?半包含于左區(qū)間,則查詢(xún)區(qū)間拆分,左端點(diǎn)不變,右端點(diǎn)變?yōu)樽蠛⒆拥挠覅^(qū)間端點(diǎn)??
  12. ????????????Query(i,?l,?node[i].right);??
  13. ????}??
  14. ????i?+=?1;?//?right?child?of?the?tree??
  15. ????if?(r?>=?node[i].left){?//?右區(qū)間有涉及??
  16. ????????if?(l?>=?node[i].left)?//?全包含于右區(qū)間,則查詢(xún)區(qū)間形態(tài)不變??
  17. ????????????Query(i,?l,?r);??
  18. ????????else?//?半包含于左區(qū)間,則查詢(xún)區(qū)間拆分,與上同理??
  19. ????????????Query(i,?node[i].left,?r);??
  20. ????}??
  21. }?
  22. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 補(bǔ)一個(gè)線段樹(shù)求約瑟夫環(huán)的代碼
  23. #include<bits/stdc++.h>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define N 200005
    int tree[N*4][2];
    int sum[N*4];
    void push(int rt)
    {sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    void build(int l,int r,int rt)
    {tree[rt][0]=l;tree[rt][1]=r;if(l==r){sum[rt]=1;return;}int m=(l+r)>>1;build(lson);build(rson);push(rt);
    }
    int update(int s,int rt)
    {sum[rt]--;if(tree[rt][0]==tree[rt][1]){sum[rt]=0;return tree[rt][0];}if(s<=sum[rt<<1])return update(s,rt<<1);else return update(s-sum[rt<<1],rt<<1|1);
    }
    int main()
    {int n,m;while(~scanf("%d %d",&n,&m)){build(1,n,1);int temp=1;int pos;for(int i=1;i<=n;i++){temp=(temp+m-1)%sum[1];if(temp==0) temp=sum[1];pos=update(temp,1);if(i<n)cout<<pos<<" ";else cout<<pos<<endl;}}
    }


總結(jié)

以上是生活随笔為你收集整理的线段树(自看)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。