luogu3834 【模板】可持久化线段树1(主席树)

关键字:线段树 可持久化

线段树:当版本(即对应的原序列的区间[1,r])一定时,每个节点的left,right下标为值域,值为其对应的原序列区间[1,r]中元素大小在值域中的元素个数。

可持久化:新版本(对应原序列[1,r])在旧版本(对应原序列[1,r-1])上建立,从树根向树叶构造,在旧版本的节点的旁边构造新版本的节点,值为旧版本节点值+1。搜索新版本树时,从新版本树根开始搜索即可。求区间第k大,同时遍历【1,l-1】对应版本树和【1,r】对应版本树,通过节点值的差来得到第k个节点。

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cmath>
#include <algorithm>
using namespace std;

#define LOOP(i,n) for(int i=1; i<=n; i++)
#define INF 0x3f3f3f3f
const int MAX_ROOT=2e5;
int Seq[MAX_ROOT];

struct Node 
{
    int Cnt;
    Node *LeftSon, *RightSon;
    Node() 
    {
        Cnt = 0;
        LeftSon = RightSon = NULL;
    }
};

Node *_roots[MAX_ROOT];
int _vCount;
int MinL, MaxR;

void Init(int l, int r) 
{
    _vCount = 0;
    MinL = l;
    MaxR = r;
    _roots[0] = new Node();
}

Node *Update(Node *prev, Node *&cur, int l, int r, int p) 
{
    cur = new Node();
    *cur = *prev;
    cur->Cnt++;
    if (l == r)
        return cur;
    int mid = (r - l) / 2 + l;
    if (p <= mid) 
    {
        if (!prev->LeftSon)
            prev->LeftSon = new Node();
        Update(prev->LeftSon, cur->LeftSon, l, mid, p);
    }
    else 
    {
        if (!prev->RightSon)
            prev->RightSon = new Node();
        Update(prev->RightSon, cur->RightSon, mid + 1, r, p);
    }
    return cur;
}

int Query(Node *prev, Node *cur, int l, int r, int k) 
{
    if (l == r)
        return l;
    int prevLeft = prev->LeftSon ? prev->LeftSon->Cnt : 0, curLeft = cur->LeftSon ? cur->LeftSon->Cnt : 0;
    int leftCnt = curLeft - prevLeft;
    int mid = (r - l) / 2 + l;
    if (k <= leftCnt) 
    {
        if (!prev->LeftSon)
            prev->LeftSon = new Node();
        return Query(prev->LeftSon, cur->LeftSon, l, mid, k);
    }
    else 
    {
        if (!prev->RightSon)
            prev->RightSon = new Node();
        return Query(prev->RightSon, cur->RightSon, mid + 1, r, k - leftCnt);
    }
}

void Update(int r) 
{
    Update(_roots[r - 1], _roots[r], MinL, MaxR, Seq[r]);
}

int Query(int l, int r, int k) 
{
    return Query(_roots[l - 1], _roots[r], MinL, MaxR, k);
}

int main() 
{
#ifdef _DEBUG
    freopen("c:\noi\source\input.txt", "r", stdin);
#endif
    int seqCnt, queryCnt, l, r, k, minL = INF, maxR = -INF;
    scanf("%d%d", &seqCnt, &queryCnt);
    LOOP(i, seqCnt) 
    {
        scanf("%d", i + Seq);
        minL = min(Seq[i], minL);
        maxR = max(Seq[i], maxR);
    }
    Init(minL, maxR);
    LOOP(i, seqCnt)
        Update(i);
    LOOP(i, queryCnt) 
    {
        scanf("%d%d%d", &l, &r, &k);
        printf("%d
", Query(l, r, k));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/headboy2002/p/8438289.html