可持久化線段樹(主席樹)新手向教程

嗯今天來講講一個高端玩意,叫可持久化線段樹。

新手向,有點耐心是一定可以懂的

知識儲備

首先你得知道線段樹是什麼,不然也不需要學這個東西
線段樹:(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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章