1. 程式人生 > >【刷題】洛谷 P3834 【模板】可持久化線段樹 1(主席樹)

【刷題】洛谷 P3834 【模板】可持久化線段樹 1(主席樹)

!= tchar 這樣的 信息 reg har mem hair define

題目背景

這是個非常經典的主席樹入門題——靜態區間第K小

數據已經過加強,請使用主席樹。同時請註意常數優化

題目描述

如題,給定N個正整數構成的序列,將對於指定的閉區間查詢其區間內的第K小值。

輸入輸出格式

輸入格式:

第一行包含兩個正整數N、M,分別表示序列的長度和查詢的個數。

第二行包含N個正整數,表示這個序列各項的數字。

接下來M行每行包含三個整數l, r, kl,r,k , 表示查詢區間[l, r][l,r] 內的第k小值。

輸出格式:

輸出包含k行,每行1個正整數,依次表示每一次查詢的結果

輸入輸出樣例

輸入樣例#1:

5 5
25957 6405 15770 26287 26465
2 2 1

3 4 1
4 5 1
1 2 2
4 4 1

輸出樣例#1:

6405
15770
26287
25957
26287

說明

數據範圍

對於20%的數據滿足:\(1 \leq N, M \leq 10\)

對於50%的數據滿足:\(1 \leq N, M \leq 10^3\)

對於80%的數據滿足:\(1 \leq N, M \leq 10^5\)

對於100%的數據滿足:\(1 \leq N, M \leq 2\cdot 10^5\)

對於數列中的所有數\(a_i\)? ,均滿足\(-{10}^9 \leq a_i \leq {10}^9\)

樣例數據說明

N=5,數列長度為5,數列從第一項開始依次為\([25957, 6405, 15770, 26287, 26465 ]\)

第一次查詢為\([2, 2]\) 區間內的第一小值,即為6405

第二次查詢為\([3, 4]\) 區間內的第一小值,即為15770

第三次查詢為\([4, 5]\) 區間內的第一小值,即為26287

第四次查詢為\([1, 2]\) 區間內的第二小值,即為25957

第五次查詢為\([4, 4]\) 區間內的第一小值,即為26287

題解

主席樹
這道題當作是我學習主席樹的開端吧,模板題
主席樹維護的是區間,但這個區間不是下標(即位置)的區間,而是權值的區間
在一個版本中

  • 區間\([l,l]\)上記錄的是\(l\)這個數出現了多少次
  • 區間\([l,r]\)上記錄的就是\(l,l+1,...,r-1,r\)
    每個數出現次數之和了

所以要先離散化
主席樹說白了就是給一個長度為\(n\)的序列建\(n\)棵權值線段樹,第\(i\)棵線段樹只存了\(a_1,a_2,a_3,...a_{i-1},a_i\)
這樣我們對於本題這樣的靜態查詢第\(k\)小,就可以差分了(想一想原理)
然後發現相鄰兩棵線段樹改變的只有一條路徑上的值,其它的值都是一樣的,那麽為了節省空間,我們把那些值一樣的存在同一個節點(即共用節點),就不用再開節點了
同時,我們稱第\(i\)棵線段樹為第\(i\)個版本
差分的話,就是
如果要查詢\([l,r]\)中的信息,那麽我們看
\(r\)棵線段樹記錄的是\(a_1,a_2,...,a_r\)中的信息
\(l-1\)棵線段樹記錄的是\(a_1,a_2,...a_{l-1}\)中的信息
它們相減後,就變成\(a_l,a_{l+1},...,a_r\)中的信息了
而它們能夠相減的依據是:每一棵線段樹每一個節點維護的內容是一樣的(只是其中的值不一樣而已)

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
#define Mid ((l+r)>>1)
#define lson l,Mid
#define rson Mid+1,r
const int MAXN=200000+10;
int n,m,A[MAXN];
struct ChairMan_Tree{
    int sum[MAXN<<5],lc[MAXN<<5],rc[MAXN<<5],cnt,root[MAXN];
    inline void init()
    {
        memset(sum,0,sizeof(sum));
        memset(lc,0,sizeof(lc));
        memset(rc,0,sizeof(rc));
        cnt=0;
    }
    inline void Build(int &rt,int l,int r)
    {
        rt=++cnt;
        sum[rt]=0;
        if(l==r)return ;
        Build(lc[rt],lson);
        Build(rc[rt],rson);
    }
    inline void Insert(int &rt,int l,int r,int last,int pos)
    {
        rt=++cnt;
        lc[rt]=lc[last];
        rc[rt]=rc[last];
        sum[rt]=sum[last]+1;
        if(l==r)return ;
        else
        {
            if(pos<=Mid)Insert(lc[rt],lson,lc[last],pos);
            else Insert(rc[rt],rson,rc[last],pos);
        }
    };
    inline int Query(int now,int last,int l,int r,int k)
    {
        if(l==r)return l;
        else
        {
            int t=sum[lc[now]]-sum[lc[last]];
            if(k<=t)return Query(lc[now],lc[last],lson,k);
            else return Query(rc[now],rc[last],rson,k-t);
        }
    };
};
ChairMan_Tree T;
std::vector<int> V;
std::map<int,int> M;
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void discre()
{
    sort(V.begin(),V.end());
    V.erase(unique(V.begin(),V.end()),V.end());
    for(register int i=1;i<=n;++i)
    {
        int pre=A[i];
        A[i]=lower_bound(V.begin(),V.end(),A[i])-V.begin()+1;
        M[A[i]]=pre;
    }
}
int main()
{
    read(n);read(m);
    T.init();
    T.Build(T.root[0],1,n);
    for(register int i=1;i<=n;++i)
    {
        read(A[i]);
        V.push_back(A[i]);
    }
    discre();
    for(register int i=1;i<=n;++i)T.Insert(T.root[i],1,n,T.root[i-1],A[i]);
    while(m--)
    {
        int l,r,k;
        read(l);read(r);read(k);
        write(M[T.Query(T.root[r],T.root[l-1],1,n,k)],'\n');
    }
    return 0;
}

【刷題】洛谷 P3834 【模板】可持久化線段樹 1(主席樹)