嗯今天來講講一個高端玩意,叫可持久化線段樹。
新手向,有點耐心是一定可以懂的
知識儲備
首先你得知道線段樹是什麼,不然也不需要學這個東西
線段樹:(https://blog.csdn.net/floatiy/article/details/80233453)
引入
現在呢我們來思考一個問題,如果題目有需要保存線段樹更改前的各個歷史版本(比如給一個數列的前n項各建一棵線段樹),我們應該怎麼存?
每個版本存一棵樹嗎?
不不不太多了會爆空間的QAQ
事實上,如果要存儲前我們只需要修改少量點就可以,因爲每兩個版本間有很多的重複部分。
圈裏面是這個區間包含的數字在數列裏出現的次數。
比如:現在我們有數列 2 1 3 4
黑色爲只存儲前1個數的線段樹
現在我們要儲存前兩個數,完全不必完全新一棵樹,可以利用原來樹上和現在要建的新樹一樣的部分,也就是如果原來樹上的某段區間在新樹中仍將保持不變,那我們就把這個點(包括它的整個子樹)連到新的根節點下面,從而避免了新建這些部分所用的空間和時間。
拿上面的圖來說,因爲數列1,2總共有兩個元素而不是原來黑色樹的一個,所以新建一個節點(最上面的紅色那個)
然後我們發現右子樹(3~4)在新樹中和原來的一樣,所以不做修改,直接把右子樹連到新的根節點上面
再看左子樹,發現發生了變化,所以創建新點,
然後以此類推,就可以得到新的線段樹(紅色)
p.s. 紅色是前兩個元素(2,1)的樹
嗯大家可以自己畫個圖模擬一下啊
代碼實現
接下來考慮如何寫代碼
首先我們需要建一棵各個值均爲0的“空樹”,這裏注意一下,我們不得不使用指針,而不是像原來那樣ls = pos << 1,rs = pos << 1 | 1了,因爲不同的歷史版本中同一個位置的點可能會有不同的編號。
Code
//by floatiy
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 200000 + 5;
int n,m;
int cnt,q;
int a[MAXN],b[MAXN],head[MAXN];
struct Tree{
int sum;
int ls,rs;
}t[MAXN << 5];
void update(int pos){
t[pos].sum = t[t[pos].ls].sum + t[t[pos].rs].sum;
return;
}
int build(int L,int R){
int pos = ++cnt;
int mid = (L + R) >> 1;
t[pos].sum = 0;
if(L < R){
t[pos].ls = build(L,mid);
t[pos].rs = build(mid + 1,R);
}
return pos;
}
int modify(int L,int R,int pre,int v){
// cout<<"DEBUG modify"<<endl;
int pos = ++cnt;
t[pos].ls = t[pre].ls;
t[pos].rs = t[pre].rs;
t[pos].sum = t[pre].sum + 1;
if(L < R){
int mid = (L + R) >> 1;
if(v <= mid) t[pos].ls = modify(L,mid,t[pre].ls,v);
else t[pos].rs = modify(mid + 1,R,t[pre].rs,v);
}
// update(pos);
return pos;
}
int query(int L,int R,int ll,int rr,int k){
if(L >= R) return L;
int mid = (L + R) >> 1;
int x = t[t[rr].ls].sum - t[t[ll].ls].sum;
if(x >= k) return query(L,mid,t[ll].ls,t[rr].ls,k);
else return query(mid + 1,R,t[ll].rs,t[rr].rs,k - x);
}
int main(){
cin>>n>>q;
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b + 1,b + 1 + n);
m = unique(b + 1,b + 1 + n) - b - 1;
head[0] = build(1,m);
for(int i = 1;i <= n;i++){
int x = lower_bound(b + 1,b + 1 + m,a[i]) - b;
head[i] = modify(1,m,head[i - 1],x);
}
// for(int i = 1;i <= cnt;i++){
// cout<<"DEBUG: "<<i<<": "<<head[i]<<" "<<t[i].ls<<" "<<t[i].rs<<" "<<t[i].sum<<endl;
// }
while(q--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int ans = query(1,m,head[x - 1],head[y],z);
printf("%d\n",b[ans]);
}
return 0;
}