給定n個正整數構成的序列,將對於指定的閉區間查詢其區間內的第k小值。 輸入格式:
第一行包含兩個正整數n、m,分別表示序列的長度和查詢的個數。
第二行包含n個正整數,表示這個序列各項的數字。
接下來m行每行包含三個整數 l, r, kl,r,k , 表示查詢區間[l, r][l,r] 內的第k小值。
輸出格式:
輸出包含k行,每行1個正整數,依次表示每一次查詢的結果
對於100%的資料滿足:\(1 \leq n, m \leq 2\cdot 10^5\)
\(1≤n,m≤2⋅10^5\)
對於數列中的所有數\(a_i\),都有\(-10^9\leq a_i\leq 10^9\)
這個題目看上去很像一道線段樹或者樹狀陣列之類的裸題,但是仔細想想,區間第\(k\)小是線段樹等資料結構維護不了的,這個時候,我們就需要引進一種新的資料結構,就是可持久化線段樹,也就是主席樹。(可持久化資料結構是可以訪問歷史版本的,這裡也可以做到,但是這裡不需要訪問歷史版本,我們只利用可持久化資料結構的思想)
主席樹的本質上是n顆值域線段樹(不知道值域線段樹的請自行轉走),對於每一顆線段樹我們都維護從序列開始到這個元素的值域(即第\(i\)顆線段樹維護的區間是第一號元素到第\(i\)號元素的值域)。
但實際上我們沒有那麼多時間和空間去維護\(n\)顆線段樹,所以我們就要想,每一顆線段樹由於值域相同,它們的形狀是完全相同的,並且對於一顆第\(i\)顆線段樹來說,它相對於第\(i-1\)顆線段樹只增加了乙個值,放在值域中,也就只有包含這個值的log個節點不同,所以對於每一顆線段樹,我們只需要新開\(log\)個節點,其它的節點就用第\(i-1\)顆線段樹的節點(如果你會可持久化陣列,就會發現這其實跟可持久化陣列很像,準確的說可持續化陣列的模型就是主席樹)。
我們先開乙個root陣列來儲存每一顆線段樹的根,對於每乙個線段樹的節點記錄它的值,左兒子和右兒子的編號,在構建第i顆線段樹時,我們要同時訪問第i-1顆線段樹,每次構建乙個節點之後,對於不包含這個新增值的兒子我們就直接將第i-1顆線段樹的相應的那個兒子作為第i顆線段樹的這個兒子。
比如說,假設離散化之後的值域是15,第i號元素是1,我們先構建根節點,然後發現這個節點左兒子的值域是12,右兒子的值域是3~5,右兒子的值域不包括1,所以右兒子就直接用第i-1顆線段樹的右兒子,而此時我們就新建乙個節點作為這個節點的左兒子,值為第i-1顆線段樹的左兒子的值+1。
int modify(int l,int r,int x,int k)
主席樹的查詢跟值域線段樹的查詢差不多,值域線段樹的查詢大家都會吧,我這裡就不再贅述,不過,主席樹每次需要同時查詢兩顆線段樹,如果我們需要查詢\([l,r]\)閉區間中第\(k\)小的值,我們就查詢第\(l-1\)顆線段樹和第\(r\)顆線段樹的資訊,由於所有線段樹維護的值域完全一樣,所以我們可以用第r顆線段樹詢問到的值減去第\(l-1\)顆線段樹的值,就可以得出\([l,r]\)閉區間的值。(注意:你查詢到的是離散之後的值,你需要輸出的是離散之前的值)
具體實現過程
int query(int l,int r,int la,int no,int k)
#includeusing namespace std;
inline int gi()
const int n=1e6+20;
struct ljq
b[n];
struct tree
t[n*5];
int cmp(ljq x,ljq y)
void work2(int l,int r,int la,int no,int x)
else
}/*這個構建主席樹的實現過程和上面略有不同,上面的更方便,是我在打帶修改的主席樹的時候寫的,這裡我懶得改了,僅做參考*/
int query(int l,int r,int la,int no,int k)
int main()
while(m--)
return 0;
}
靜態主席樹
首先主席樹解決什麼樣的問題?最經典的問題就是 區間第k小問題 也就是指定乙個區間,要求求出這個區間中第k小的數字 在搞懂什麼是主席樹之前,我們要先對權值線段樹有一定的了解,下面我們就先說一下權值線段樹,然後詳細說一下主席樹以及主席樹程式的實現.權值線段樹 每個葉子節點的數值表示的是 陣列中含有這個數...
靜態主席樹
主席樹 對於序列的每乙個字首建一棵以序列裡的值為下標的線段樹 所以要先離散化 記錄該字首序列裡出現的值的次數 記離散後的標記為1 n 下面值直接用1 n代替 對於區間 x,y 的第k大的值,那麼從root x 1 root y 開始,t root y 1,mid root x 1 1,mid t表示...
主席樹(靜態區間k小值)
主席樹是一種可持久化動態線段樹,常用於求區間k小值問題。其本質為建n棵字首和線段樹,也就是n個版本,第i個版本囊括區間 1 i 這裡的i指的是第i個數。那麼求第l到第r個數的區間k小時,便可以轉化為求query r query l 1 對於乙個序列,進行排序和unique去重,得到乙個長度為s的元素...