首先主席樹解決什麼樣的問題?最經典的問題就是:區間第k小問題(也就是指定乙個區間,要求求出這個區間中第k小的數字)
在搞懂什麼是主席樹之前,我們要先對權值線段樹有一定的了解,下面我們就先說一下權值線段樹,然後詳細說一下主席樹以及主席樹程式的實現......
權值線段樹:每個葉子節點的數值表示的是:陣列中含有這個數值的個數是多少,父親節點的數值=兩個兒子節點數值的相加。
可以理解為:假設乙個兒子表示數字x,另乙個兒子表示數字y,兒子節點表示的是區間[x,x]、[y,y]中含有的數量,那麼其兩個的父親節點表示的就是區間[x,y]之間含有的數量
假設陣列:5 6 5 8 9 8 2 8 6,首先我們要對這個陣列進行離散化,離散化後的陣列為:2 3 2 4 5 4 1 4 3
那麼他的權值線段樹表示為:
葉子節點下面的數字表示葉子節點表示的區間[x,x]
這就是乙個權值線段樹,當我們查詢陣列中第5大的數字的時候,我們先和各節點的左兒子數字比較,8大於5,那麼發現左兒子中含有8個數字,右兒子含有1個數字,那麼第五位一定在左兒子中,那麼進入左兒子。向下看,看當前節點的左兒子,發現左兒子為數值為3,那麼5大於3,說明第五位不在左兒子中,那麼一定就在右兒子中,那麼在右兒子中的排名為:5-3=2,也就是在右兒子中找到第二小的數值,進入右兒子。再次判斷當前節點的左兒子,左兒子數值為2,2小於等於2,那麼第二小的數字就在當前節點的左兒子中,那麼進入左兒子,因為當前節點已經為葉子節點,那麼第5小的數值就是離散化後的3,離散化之前那麼就是6
主席樹實質上就是可持久化的權值線段樹,可持久化指的是他儲存了這棵樹的所有歷史版本,那麼實現可持久化最簡單的方法就是:每來乙個新的節點n,我們就在區間[1,n]之間建立一棵權值線段樹,那麼如果這樣做的話,記憶體肯定會超限,所以我們這裡要做的就是盡量地去使用之前的權值線段樹版本,只去更新當前節點插入後更改的節點即可
我們會發現:每插入乙個節點,更新的只有根節點到更新葉子節點這條路徑上的數值,這條路徑上的數值都去執行+1操作,那麼其餘的我們使用之前版本的權值線段樹就好
比如:第一張圖的紅色節點要新增入乙個新的數值,那麼更新的節點只有如第二個圖所示:
我們只需要將更新紅色的節點即可(新建節點),黑色的節點只需要指向前面那一棵樹的節點
當我們求解區間[l,r]之間的第k小的數值的時候,我們只需要使用第r個線段樹減第l-1個線段樹(這裡可以動手畫一下,比如上面兩棵權值線段樹相減),那麼這個線段樹就是區間[l,r]之間的線段樹了,求解第k小的數值就和上面權值線段樹描述的步驟是相同的。
poj 2104
#include #include #include #include using namespace std;
const int maxn = 1e5+6;
int n,m,cnt,root[maxn]; //root表示很節點的編號
int x,y,k,a[maxn];
struct node
t[maxn*40];
vectorv; //離散化操作
int getid(int x)
/*更新操作:也就是從根節點到要更新的葉子節點,我們只是尋找這個路徑上的點,其餘的不發生改變(包含節點的左兒子或者右兒子),當更新這路徑上的節點的時候,
我們每更新乙個節點,就要新建乙個節點,當前節點對應於之前乙個狀態的sum值要+1,一直到葉子節點!!!
*/void update(int l,int r,int &x,int y,int pos) //pos:更新的位置 x:當前更新你的權值線段樹
/*查詢操作:查詢在根節點狀態x-y之間,的第k大的數字
*/int query(int l,int r,int x,int y,int k)
int main()
sort(v.begin(),v.erase(unique(v.begin(),v.end()),v.end())); //
for(int i =1;i <= n;i ++)
update(1,n,root[i],root[i-1],getid(a[i])); //每來乙個節點,就從起始節點到當前節點建立一棵權值線段樹,但是我們只根節點到當前節點路徑上的節點,別的可以直接鏈結到即可!
//建立的第i個權值線段樹,需要更新的位置為getid(a[i])
for(int i = 1;i <= m;i ++)
return 0;
}
靜態主席樹
主席樹 對於序列的每乙個字首建一棵以序列裡的值為下標的線段樹 所以要先離散化 記錄該字首序列裡出現的值的次數 記離散後的標記為1 n 下面值直接用1 n代替 對於區間 x,y 的第k大的值,那麼從root x 1 root y 開始,t root y 1,mid root x 1 1,mid t表示...
深度剖析主席樹(靜態)
主席樹 chairman tree 據說是被chairman傳開的 它是一種專門用來實現求區間第k大這類操作的資料結構 q 這不是分塊輕鬆解決的事嗎?a 是的,但是分塊時間為 q 這是不是很難,複雜度極高?a nonono,複雜度一點都不高,前置知識只有動態開點線段樹 q 那我不會怎麼辦?a 右 行...
主席樹 靜態 的輕鬆入門
n i 1 85 kj 0i2 i 1n 58 j 0ki2 emmm 最近入門了主席樹,感覺其實不是很難,主要理解了就很簡單了 畢竟 這麼短 給出乙個數列,求區間 l r 之間的第 k 大值利用數列中 n 個資料建立 n 棵樹,其中第 i 棵樹維護 1 i 這個字首內的資料資訊 第 i 棵樹中的每...