主席樹是線段樹的公升級版,學主席樹之前先把線段樹弄會,線段樹講解參見-->>傳送門
主席樹可以解決 求區間不同數的個數、靜態區間第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 和...