求區間第k大有很多種方法比如可持久化線段樹,分桶法和平方分割 當然,我們還可以用歸併樹;(請原諒蒟蒻才曉得歸併樹,寫的不好)。
我們把數列用線段樹維護起來。線段樹的每個節點都儲存了對應區間排好序後的結果。在這之前我接觸到的線段樹上面儲存的都是數值,而這次有所不同,每個節點儲存的是數列。
如圖(畫的醜,勿噴):
建立線段樹的過程和歸併排序類似,每個節點的數列是其兩個兒子的數列進行排序的結果。建樹的複雜度是o(nlogn);其實這棵樹也就是歸併排序的完整體現。
那麼要找區間第k大,我們通過二分答案來判斷。就像分桶法和平方分割 一樣,只是判定方式有區別。
要找區間內比當前x小的數的個數,我們可以用這棵樹來查詢。
- 如果所給的區間和當前節點的區間沒有交集,返回0;
- 如果所給的區間完全包含了當前區間,那麼用二分查詢的方法來對當前節點上儲存的數列進行查詢(比如可以用upper_bound);
- 否則對兩個兒子進行遞迴計算後求和返回;
由於對於線段樹需要logn個區間,每個區間查詢需要logn次,查詢一次的時間複雜的為o((logn)^2);
所以總的時間複雜度是o(nlogn+m(logn)^3);
注:merge函式的作用是:將兩個有序的序列合併為乙個有序的序列。函式引數:merge(first1,last1,first2,last2,result,compare);如果要合併的是vector要事先用resize。#include
#include
#include
#include
#include
using
namespace
std;
#define lch (rt<<1)
#define rch ((rt<<1)+1)
const
int maxn=100005;
int a[maxn],nums[maxn];
vector
data[maxn*4];
int n,m;
void build(int rt,int l,int r)
else
//for(int i=0;i// printf("%d ",data[rt][i]);
//printf(":%d %d %d\n",rt,l,r);
}int check(int i,int j,int x,int rt,int l,int r)
int main()
sort(nums+1,nums+n+1);
build(1,1,n);
for(int i=1;i<=m;i++)
printf("%d\n",nums[ub]);}}
區間第k大(主席樹)
學了一下主席樹模板題,當初看了網上的主席樹講解都沒有看懂,後面看了嗶哩嗶哩的uestc的主席樹,終於看懂了思想。每次更新的複雜度都為logn。每次更新的話就是對要更新的點路徑上的點重新更加乙個,然後進行對沒有影響的那些進行連邊。然後用乙個root記錄每乙個線段樹的根節點下標。include incl...
主席樹區間第K大
主席樹的實質其實還是一顆線段樹,然後每一次修改都通過上一次的線段樹,來新增新邊,使得每次改變就改變logn個節點,很多節點重複利用,達到節省空間的目的。1.不帶修改的區間第k大。hdu 2665 模板題 1 include2 using namespace std 3 define fopen fr...
區間第k大
問題描述 給定乙個序列,每次詢問序列中第l個數到第r個數中第k大的數是哪個。輸入格式 第一行包含乙個數n,表示序列長度。第二行包含n個正整數,表示給定的序列。第三個包含乙個正整數m,表示詢問個數。接下來m行,每行三個數l,r,k,表示詢問序列從左往右第l個數到第r個數中,從大往小第k大的數是哪個。序...