可持久化數據結構入門


什么是可持久化數據結構呢?

簡單一點來說,就是能支持訪問以往某個版本的數據的數據結構,當然我的總結並沒有那么貼切……

我們以這樣一個事來引入吧!記得上學期的時候上數學課學統計,聽輝哥在上面講課,我在下面突然想到,能否寫一種數據結構,能夠快速訪問每一個給定區間的中位數是多少?

考慮最暴力的做法就是每次sort取中間,但這樣顯然太慢了,然后如果在每個區間都建一棵權值線段樹來維護,那空間又太大了。

所以我們引入一個新的強勢數據結構——可持久化線段樹(主席樹)來解決這個問題!

為啥叫主席樹呢?傳說發明這種數據結構的神犇黃嘉泰,因為他在考試的時候不會寫歸並樹就寫了主席樹這種東西替代歸並樹,並成功讓廣大OIer使用了這種數據結構,把歸並樹扔進了垃圾箱。因為他的名字縮寫HJT也是當時chairman的名字縮寫,故稱為主席樹。

言歸正傳。其實主席樹相當於在每一個節點維護了一棵線段樹(不是真的建了出來!)主席樹節點中維護的值,是1-i之間這個區間內出現了數的次數。然后當我們查詢的時候,就是利用到了前綴和的思想。

首先我們先偷大神的圖來模擬一下建樹的過程:感謝bestFY大神

7 1

1 5 2 6 3 7 4

2 5 3 

 然后開始插入,把經過的每一個節點的v++。

這樣一直插入,直到最后把所有數插入完畢。

好的,那我們現在要查詢。怎么查詢呢?既然要查詢[2,5]區間內第3大的數,那么就先把第一棵和第5棵線段樹拿出來。

 

這時候我們驚奇的發現,對於每一個區間,我們用第5棵樹中該區間的權值減去第一棵樹中改區間的權值,發現delta就是在該范圍的數在[2,5]中出現的次數!

這里其實是用到了前綴和的思想。仔細思考一下即可知。

這樣的話我們就能在這棵delta樹(這是沿用了兔哥的叫法)上進行k大值查詢。只要像正常的權值線段樹一樣,如果當前數的排名大於左子樹大小,就在右子樹中找,否則在左子樹中找就可以了。

不過實際上這棵樹並不是這個樣子的,上面的樣子只是比較好理解,我們實際在操作的時候每修改一次就要新建一條鏈,不過我們只處理和本次修改操作有關的,無關的直接和原樹共用即可。

然后我們就完成了。

傳送門 看一下代碼。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int> 
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 200005;
const int N = 1000005;
 
int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

struct seg
{
    int lson,rson,v;
}t[N<<2];

int n,m,idx,a[M],h[M],tot,root[M],x,y,z;

void build(int &p,int l,int r)
{
    p = ++idx;
    if(l == r) return;
    int mid = (l+r) >> 1;
    build(t[p].lson,l,mid),build(t[p].rson,mid+1,r);
}

void modify(int old,int &p,int l,int r,int val)
{
    p = ++idx;
    t[p].lson = t[old].lson,t[p].rson = t[old].rson,t[p].v = t[old].v + 1;
    if(l == r) return;
    int mid = (l+r) >> 1;
    if(val <= mid) modify(t[p].lson,t[p].lson,l,mid,val);
    else modify(t[p].rson,t[p].rson,mid+1,r,val);
}

int query(int old,int now,int l,int r,int k)
{
    if(l == r) return l;
    int sum = t[t[now].lson].v - t[t[old].lson].v;
    int mid = (l+r) >> 1;
    if(k <= sum) return query(t[old].lson,t[now].lson,l,mid,k);
    else return query(t[old].rson,t[now].rson,mid+1,r,k-sum);
}

int main()
{
    n = read(),m = read();
    rep(i,1,n) a[i] = h[i] = read();
    sort(h+1,h+1+n);
    tot = unique(h+1,h+1+n) - h - 1;
    rep(i,1,n) a[i] = lower_bound(h+1,h+1+tot,a[i]) - h;
    build(root[0],1,tot);
    rep(i,1,n) modify(root[i-1],root[i],1,tot,a[i]);
    rep(i,1,m)
    {
    x = read(),y = read(),z = read();
    printf("%d\n",h[query(root[x-1],root[y],1,tot,z)]);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM