主席樹學習筆記 講解

2021-08-25 11:44:05 字數 2485 閱讀 3993

主席樹是線段樹的公升級版,學主席樹之前先把線段樹弄會,線段樹講解參見-->>傳送門

主席樹可以解決  求區間不同數的個數、靜態區間第k大 等經典問題。

主席樹記錄了構建線段樹的每個步驟,可以理解為多個線段樹的壓縮版(公用相同的節點)。

我們可以考慮每新增乙個點,都將原來的線段樹複製一遍,在新樹上進行修改,這樣就保留了過程中每乙個中間步驟。

但是這樣會浪費大量的時間和空間。我們發現,線段樹更新乙個葉子的時候,只修改了從根節點到當前葉子這條路上的logn個節點。其餘的分叉根本沒有被改變,理論上講也就不需要將其他沒有改變的節點備份。

主席樹就是根據這個思路壓縮空間和時間的。

見下圖:

左側是一棵線段樹,要改變葉子7時,只需要將被改變的一條鏈做修改拷貝,其餘沒有改變的節點依然共用之前的值,這樣每次更新葉子只需要付出logn的時間和空間。全部更新完畢後,我們就得到了n+1棵線段樹,時間空間複雜度僅為nlogn。

來自kuangbin板子(區間不同數字個數)

建立:主席樹的建立僅僅是建立一棵空的線段樹用於初始化。

#include

using namespace std;

int const maxn=1e5+5;

int const maxroot=maxn*100;//開四倍的空間應該就夠了吧 為啥要開100

int n,q; 

int tot=0;//分配空間 

int a[maxn];//存放原始資料 

int t[maxn];//根 

int lson[maxroot];//左孩子索引 

int rson[maxroot];//右孩子索引 

int c[maxroot]; //節點值 

int build(int l,int r)

return root;

}

更新

建立logn個節點,並索引至上乙個狀態的節點

int update(int root,int pos,int add)

else 

} //l==r 已經到達葉子 最後將葉子跟新值

c[newroot]=c[root]+add; 

return tmp;   //返回新樹根 

查詢

選擇好要在哪棵樹上查詢,然後跟線段樹完全相同的操作。

int query(int root,int pos)

else

} return ret+c[root];//最後加上葉子自己。 

}

完整**(求區間不同數的個數)

(解釋在後面)

#include

using namespace std;

int const maxn=30010;

int const maxroot=maxn*100;

int n,q; 

int tot=0;//分配空間 

int a[maxn];//存放原始資料 

int t[maxn];//根 

int lson[maxroot];//左孩子索引 

int rson[maxroot];//右孩子索引 

int c[maxroot]; //節點值 

int build(int l,int r)

return root;

}int update(int root,int pos,int add)

else 

c[newroot]=c[root]+add; 

} return tmp;   //返回新樹根 

} int query(int root,int pos)

else

} return ret+c[root];//最後加上葉子自己。 

}int main()

t[n+1]=build(1,n);  //將初始的線段樹放到n+1的位置上

mapmp;  //記錄下每個值最靠左邊出現的位置。 

mp.clear();

for(int i=n;i>=1;i--)

else 

mp[a[i]]=i;//更新最後出現的位置。 }/*

上面的for迴圈中,重複的數字的位置,只有最左邊的位置置1,其他位置加-1(也就是置0了) 

*/scanf("%d",&q);

while(q--) }} 

解釋一下為什麼這麼做就可以統計區間不同數的個數:

我們倒著更新主席樹,那麼t[i]所在的樹只有i到n的值更新了,左邊的值還空著(為0)

而且主席樹更新的時候重複數字只保留了最左端為1,其餘均置0了,所以對區間求和就可以達到求不同數個數的目的 ,由於l之前的全為0,區間求和也就等價於取r的字首和。

不需要的不更新的思想。

主席樹學習筆記

問題 給定乙個n個數的序列,q次詢問第x個數到第y個數中的第k最值。我們假定是第k小。為了使討論更加簡便,我們假定序列的每個數都是不大於n的正整數。當然一般題目中元素範圍很大,但是可以用離散化預處理來做到這一點。考慮乙個比較高階的做法 令g i j 為前i個數中,值為j的數的個數。很容易用o n 2...

主席樹 學習筆記

主席樹就是權值線段樹的乙個集合 模板題為求某個區間的第 k 大,權值線段樹也有求第 k 大這個功能,但是不能維護區間,只能求整個全域性第 k 大 所以學習這個之前,務必先搞懂權值線段樹 所以 都是這道題的 模板 可持久化線段樹 1 主席樹 1 先從建樹開始說起 int build int l,int...

主席樹學習筆記

學習博文 主席樹總結 p3834 模板 可持久化線段樹 2 主席樹 給出乙個序列,每次詢問給定區間內第k小的值。主席樹模板。考慮最簡單的情況,也就是查詢區間固定。首先對資料進行離散化,用線段樹維護。每個節點對應離散化後值域的數的總個數 size.從上到下進行查詢時,判斷當前節點左子樹的 size 和...