求區間第k小(主席樹)
阿新 • • 發佈:2018-12-21
區間第k小
題目描述
如題,給定個正整數構成的序列,將對於指定的閉區間查詢其區間內的第小值。
輸入格式
第一行包含兩個正整數、,分別表示序列的長度和查詢的個數。
第二行包含個正整數,表示這個序列各項的數字。
接下來行每行包含三個整數 ,表示查詢區間內的第小值。輸出格式
輸出包含行,每行個正整數,依次表示每一次查詢的結果
輸入樣例
輸出樣例
說明
資料範圍:
對於20%的資料滿足:
對於50%的資料滿足:
對於80%的資料滿足:
對於100%的資料滿足:
對於數列中的所有數,均滿足樣例資料說明:
,數列長度為,數列從第一項開始依次為
第一次查詢為區間內的第一小值,即為
第二次查詢為區間內的第一小值,即為
第三次查詢為區間內的第一小值,即為
第四次查詢為區間內的第二小值,即為
第五次查詢為區間內的第一小值,即為
先簡化問題,如果求的是多次詢問同一段區間的第小,我們可以怎麼做?
用權值線段樹對離散化後的數列進行統計,並在線段樹上二分,若小於等於的數的個數,則答案一定,否則答案一定。
在權值線段樹上二分就可以了。
現在我們要求任意一段區間的第小,相當於要求出任意一段的權值線段樹。有因為是靜態的,很容易想到字首和。我們對每一個字首都開一棵建立在離散化陣列上的權值線段樹,若詢問 ~ 區間內的第小,只要將第棵線段樹上的每個節點的數值第棵線段樹上的每個節點的數值就能生成目標線段樹。
但是我們會發現一個問題,那就是將一整棵線段樹賦值的時間與空間複雜度都是無法承受的。因此,主席樹最最精妙的一點就是對於每一個時刻都保留前一個時刻樹的整體,而只修改一條鏈資料。這樣,預處理時間和空間複雜度都降到了。
程式碼
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 200005;
const int maxt = 7500000;
int n;
int a[maxn];
int read()
{
char ch = getchar(); bool f = 1;
while(ch < '0' || ch > '9') f &= ch != '-' , ch = getchar();
int res = 0;
while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48) , ch = getchar();
return f ? res : -res;
}
void write(int x)
{
if(x < 0) x = -x , putchar('-');
int len = 0 , res[15];
for(;x;res[++len] = x % 10 , x /= 10);
for(int i = len;i >= 1;i--) putchar(res[i] + 48);
if(!len) putchar('0');
}
struct inm{int ls , rs , cnt;};
struct CT
{
private:
int m;
int b[maxn];
int cnt;
inm t[maxt]; int rt[maxn];
void build(int &p , int l , int r)
{
p = ++cnt;
if(l == r) return;
int mid = l + r >> 1;
build(t[p].ls , l , mid);
build(t[p].rs , mid + 1 , r);
}
void update(int &p , int pre , int l , int r , int x)
{
p = ++cnt;
t[p] = t[pre];
t[p].cnt++;
if(l == r) return;
int mid = l + r >> 1;
if(x <= mid) update(t[p].ls , t[pre].ls , l , mid , x);
else update(t[p].rs , t[pre].rs , mid + 1 , r , x);
}
int kth(int p , int pre , int l , int r , int k)
{
if(l == r) return l;
int mid = l + r >> 1 , num = t[t[p].ls].cnt - t[t[pre].ls].cnt;
if(num >= k) return kth(t[p].ls , t[pre].ls , l , mid , k);
else return kth(t[p].rs , t[pre].rs , mid + 1 , r , k - num);
}
public:
void clear()
{
cnt = 0;
for(int i = 1;i <= n;i++) rt[i] = 0;
}
void init()
{
for(int i = 1;i <= n;i++) b[i] = a[i];
sort(b + 1 , b + n + 1);
m = unique(b + 1 , b + n + 1) - b - 1;
build(rt[0] , 1 , m);
for(int i = 1;i <= n;i++)
{
int loc = lower_bound(b + 1 , b + m + 1 , a[i]) - b;
update(rt[i] , rt[i - 1] , 1 , m , loc);
}
}
int query(int l , int r , int k){return b[kth(rt[r] , rt[l - 1] , 1 , m , k)];}
}ct;
int main()
{
n = read();
int q = read();
for(int i = 1;i <= n;i++) a[i] = read();
ct.clear() , ct.init();
while(q--)
{
int l = read() , r = read() , k = read();
write(ct.query(l , r , k));
putchar('\n');
}
return 0;
}