主席樹


主席樹是一種可持久化線段樹、其發明者orz 黃嘉泰 拼音縮寫與某屆主席一樣、於是這個數據結構被戲稱為主席樹。

所謂的“持久化數據結構”、就是保存這個數據結構的所有歷史版本、同時利用它們之間的共用數據減少時間和空間的消耗。

由於線段樹在區間長度固定的情況下結構都是一致的、主席樹能夠通過兩顆線段樹相減來得到某一區間的信息。

至於主席樹的作用、其能夠查詢不修改的區間K大值、區間不同數的個數、套個樹狀數組還能查詢動態K大值......

給出幾篇文章以便學習 ==> 鏈接II、鏈接II、鏈接III

 

一些題目 :

HDU 2665 (可作為模板使用)

題意 : 給出一個整數序列、有若干個問詢、每次問詢 (L, R, K) 表示 L~R 區間內第 K 大的值是多少

分析 : 比較裸的主席樹題目

首先先對於每個前綴按權值建出主席樹、然后問詢的時候就可以通過減法得到區間 (L, R) 的信息

由於存儲的是值域信息、查詢K大值的時候就判斷左右子區間的元素個數與K的大小便能知道往哪個方向走

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
struct NODE{
    int sum, L, R;
    NODE(){};
    NODE(int _sum, int _L, int _R):
        sum(_sum),L(_L),R(_R){};
}T[maxn*18]; int Tcnt = 0;

int root[maxn];
int arr[maxn], N;
int uni[maxn], uniLen;

int newNode(int sum, int L, int R)
{
    T[++Tcnt] = NODE(sum, L, R);
    return Tcnt;
}

inline void Insert(int &root, int pre, int pos, int L, int R)
{
    root = newNode(T[pre].sum+1, T[pre].L, T[pre].R);
    if(L == R) return ;
    int M = L + ((R-L)>>1);
    if(pos <= M) Insert(T[root].L, T[pre].L, pos, L, M);
    else Insert(T[root].R, T[pre].R, pos, M+1, R);
}

int Kth(int x, int y, int L, int R, int K)
{
    if(L == R) return L;
    int M = L + ((R-L)>>1);
    int L_sum = T[T[y].L].sum - T[T[x].L].sum;
    if(K <= L_sum) Kth(T[x].L, T[y].L, L, M, K);
    else Kth(T[x].R, T[y].R, M+1, R, K - L_sum);
}

int main(void)
{
    int nCase;
    scanf("%d", &nCase);
    while(nCase--){

        T[0] = NODE(0, 0, 0);///將 0 號節點的左右子樹指向自己
        Tcnt = root[0] = 0;///便不用顯式建樹

        int Q;
        scanf("%d %d", &N, &Q);
        for(int i=1; i<=N; i++){
            scanf("%d", &arr[i]);
            uni[i-1] = arr[i];
        }

        sort(uni, uni+N);
        uniLen = unique(uni, uni+N) - uni;///離散化

        for(int i=1; i<=N; i++){
            int pos = lower_bound(uni, uni+uniLen, arr[i]) - uni;
            pos++;
            Insert(root[i], root[i-1], pos, 1, uniLen+1);
        }

        int l, r, k;
        while(Q--){
            scanf("%d %d %d", &l, &r, &k);
            int pos = Kth(root[l-1], root[r], 1, uniLen+1, k);
            printf("%d\n", uni[--pos]);
        }
    }
    return 0;
}
View Code

 

SPOJ D-QUERY

題意 : 給出 n 個整數、每次問詢一個區間 (L, R) 問這個區間內不同數的個數是多少?

分析 :

這題很久之前用線段樹離線做過 ==> Click here

如果使用主席樹的話就能做到在線回答問詢

具體做法的核心思路其實和線段樹離線的時候差不多

但是這里主席樹存儲的並不是值域信息、而是區間具體每個位置是否包含一種數

也就是這題主席樹區間代表的信息和普通線段樹所代表的信息一樣

主席樹每次按照前綴建樹、建樹的時候保證每一種數只保留最右邊的位置

然后對於問詢 (l, r) 只要在 root[r] 這顆樹上查詢端點 l 右邊的所有 sum 值的和即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
struct NODE{
    int sum, L, R;
    NODE(){};
    NODE(int _sum, int _L, int _R):
        sum(_sum),L(_L),R(_R){};
}T[maxn*18]; int Tcnt = 0;

int root[maxn];
int arr[maxn], N;

int newNode(int sum, int L, int R)
{
    T[++Tcnt] = NODE(sum, L, R);
    return Tcnt;
}

inline void Insert(int &root, int pre, int pos, int val, int L, int R)
{
    root = newNode(T[pre].sum+val, T[pre].L, T[pre].R);
    if(L == R) return ;
    int M = L + ((R-L)>>1);
    if(pos <= M) Insert(T[root].L, T[pre].L, pos, val, L, M);
    else Insert(T[root].R, T[pre].R, pos, val, M+1, R);
}

int query(int rt, int pos, int L, int R)
{
    if(L == R) return T[rt].sum;
    int ret = 0;
    int M = L + ((R-L)>>1);
    if(pos <= M) ret += query(T[rt].L, pos, L, M) + T[T[rt].R].sum;///遞歸進入左邊區間的時候、要把右邊區間的和加上
    else ret += query(T[rt].R, pos, M+1, R);
    return ret;
}

map<int, int> mp;
int main(void)
{
    T[0] = NODE(0, 0, 0);
    root[0] = 0; Tcnt = 0;
    scanf("%d", &N);
    for(int i=1; i<=N; i++) scanf("%d", &arr[i]);
    for(int i=1; i<=N; i++){
        if(mp.count(arr[i])){
            int tmpRoot;
            Insert(tmpRoot, root[i-1], i, 1, 1, N);
            Insert(root[i], tmpRoot, mp[arr[i]], -1, 1, N);
        }else Insert(root[i], root[i-1], i, 1, 1, N);
        mp[arr[i]] = i;
    }

    int Q;
    scanf("%d", &Q);
    while(Q--){
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d\n", query(root[r], l, 1, N));
    }
    return 0;
}
View Code

 


免責聲明!

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



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