自己是沒有寫主席樹教程的想法啦,畢竟網上那麼多資料,寫那些東西的人的水平比我不知道高到**去了,而且主席樹的用法不僅僅是區間第k大,好多用法我這個弱菜根本就不知道,哪有什麼寫教程的資格,不過之前隊友問過我對主席樹的理解(幫助他入門)。當時自己根據自己的想法寫了點東西,就想不如把這些東西放在部落格上好了,也是逼自己以後多做一些主席樹的題目。
先把坑挖在這裡了啊……
題意:給你乙個含n個數的序列,接下來有m個詢問,每次詢問你原序列[l,r]區間上第k大的數是哪乙個
思路:就是主席樹的裸題,所以我直接下構造方法好了
主席樹又叫可持久化線段樹,可持久化的意思大致是指這棵線段樹可以存下自己以前的狀態,也就是說即使我們更改了這棵線段樹,更改前的內容還是被存在某個地方。
首先我們要有乙個普通的線段樹(每個葉子節點代表第幾大的數)。
接下來開始從原序列最後乙個點開始,用序列上的點乙個乙個更新線段樹。更新的方法就是走到該點在原線段樹處的位置,並把更新時的路徑作為新線段樹的一部分記錄下來。
所以更新時路徑上的每個點都要作為線段樹的乙個新節點覆蓋在原來的節點上(請自行想象乙個立體的圖形…………一棵好好的線段樹,某節點上面疊了好多節點的樣子),然後這些新節點的左右兒子,乙個是在更新路徑上的新節點,另外乙個就是它所覆蓋的原節點的左(或者是右)兒子。
這裡還有乙個值val,或者c或者其他什麼的,入門時可以簡單理解為某節點和被它蓋住的幾個節點所處路徑被走過了幾次。
最後是查詢操作,查詢某區間排序後的第k個點,就是像二分查詢一樣在給定區間找第k個點的,所給區間的右端點所處線段樹為底線,因為它是更新時最先出現的點,(建樹後的更新是從右往左更新的,上文有提到)左端點為上界,因為它是最後出現的點(再想想乙個立體的圖形 …………一棵線段樹的乙個節點被乙個節點蓋著)。然後如果上界被走過的次數減去下界的次數(這個次數就是左兒子代表區間存的數字的個數)比k大的話,那麼第k大的數就是在左兒子所代表區間裡的第k大數,反之在右兒子所代表區間的第l大數,l=k-左兒子代表區間存的數字的個數。當所給區間被我們縮小到乙個數時,這個數就是答案了。
說的可能不好,詳見**
#include#include#include#include#include#includeusing namespace std;
#define ms(x,y) memset(x,y,sizeof(x))
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&(-x))
typedef long long ll;
inline void fre1()
inline void fre2()
const int maxn=100000+10;
const double eps=1e-8;
int val[maxn*30],lson[maxn*30],rson[maxn*30];
int tot;
int a[maxn],b[maxn],idx[maxn];
int n,m;
int build(int l,int r)
return root;
}int update(int root,int pos,int v)
else
val[newroot]=val[root]+v;
} return ret;
}int query(int left_root,int right_root,int k)
else
} return l;
}int main()
while(q--)
} return 0;
}
主席樹基礎
主席樹明明是個很棒的東西,可惜網上卻很難找到很好的模板 講解 包括卿學姐的講解的模板,看得雲裡霧裡,就感覺模板不太對勁,怎麼邊界用的是離散前的?直至找到 發現邊界確實直接用離散後的就行,畢竟是實質是權值線段樹。這是第一篇,講模板,poj2104,無修改的主席樹模板,求區間第k大,主席樹的典例 首先是...
指標主席樹簡單介紹 第k小數
題面簡介 給乙個長度為n n 2e5 陣列序列 ai 1e9 有m m 2e5 次查詢,每次查詢區間 l,r 中第k小的數。還是指標香,雖然常數大了一點,但實現起來速度能快不少 前置知識 知道陣列主席樹的寫法,或者差不多知道主席樹的原理。需要會一點指標。主席樹,即可持久化線段樹,可以記錄線段樹的多個...
主席樹 初學
現在才開始學主席樹 弱 不過不帶修改的話 還是很簡單的嘛。或者說應該叫可持久化線段樹?首先對數的區間進行離散化,這樣下面的a i 都預設為離散化以後的結果了。對於每個1.i開乙個線段樹,對於這個線段樹中的每乙個節點 l,r 表示1.i中在 l,r 中的數的個數。顯然這n個線段樹的形態大小是完全一樣的...