1. 程式人生 > >主席樹學習--入門

主席樹學習--入門

      本篇文章,講講主席樹入門,以及區間第K大,或者區間第K小。學習主席樹前提是要學習線段樹,主席樹也就是多棵線段樹,每次更新都需要記錄之前的線段樹,如果每次新建一棵線段樹。比較浪費空間時間。因為每次更新一個點只會修改一條樹鏈。其他的樹節點資訊不改變,所以可以公用一些節點。這就是可持久線段樹思想。

大致思路我就不再重複了,整理了一些學習資料。演算法講堂-主席樹,這個視訊是UESTC某位大佬講解的。很好理解。配上區間第K小這個題目。練練手。這是題目連結,主體演算法推薦看這個,細節部分請看程式碼上面的註釋。

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long LL;
const int INF=2e9+1e8;

const int MOD=1e9+7;
const int MAXSIZE=1e6+5;
const double eps=0.0000000001;
void fre()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
}
#define memst(a,b) memset(a,b,sizeof(a))
#define fr(i,a,n) for(int i=a;i<n;i++)

const int maxn=1e5+5;
struct Tree
{
    int l,r,sum;
}T[maxn*40];//線段樹區間統計,sum代表在這個區間數的個數。
int root[maxn],a[maxn],sz,cnt,lisan[maxn],num[maxn];
/**    陣列說明 : 
root:代表每個歷史版本線段樹的根節點位置。
lisan:數字比較大,線段樹不能夠開那麼大,所以需要離散處理一下。
cnt :用作開闢新的樹節點。
sz : 離散後數的個數。
*/
int getid(int x) //對於較大的數,離散後的數值。
{
    return lower_bound(lisan+1,lisan+sz+1,x)-lisan;
}
/** update函式介紹
l,r 代表線段樹遞迴的區間,x代表前一棵樹的節點位置,y是後面的節點位置。
在遞迴的過程中,將需要修改的樹節點複製到新開闢節點,改變自己的sum,也就是自加1,順便改變上一個的孩子節點
所以傳參是引用傳參;
*/
void update(int l,int r,int x,int &y,int pos)
{
    T[++cnt]=T[x],T[cnt].sum++,y=cnt;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos>mid) update(mid+1,r,T[x].r,T[y].r,pos);
    else update(l,mid,T[x].l,T[y].l,pos);
}
/**  query函式介紹
因為是查詢第K小,所以在查詢時候只需要看左邊孩子節點,兩棵線段樹sum做差,便得到這個區間的值
比如 root[R]-root[L-1] ,則代表區間 [L,R] 的數的統計
所以 S=(R線段樹左孩子的sum)-(L-1線段樹左孩子的sum) 如果 S>=K(第K小),所以第K小肯定在左兒子節點,
否則,右節點,並且在右邊區間再找剩下的 K-S,即可。
*/
int query(int l,int r,int x,int y,int k)
{
    if(l==r) return l;
    int sum=T[T[y].l].sum-T[T[x].l].sum;
    int mid=(l+r)>>1;
    if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);
    else return query(mid+1,r,T[x].r,T[y].r,k-sum);
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),num[i]=a[i];
    sort(a+1,a+1+n);
    sz=0,lisan[++sz]=a[1];
    for(int i=2;i<=n;i++) if(a[i]!=a[i-1]) lisan[++sz]=a[i];
    //show(sz);
    for(int i=1;i<=n;i++)
    {
        //printf("getid=%d ",getid(a[i]));
        update(1,sz,root[i-1],root[i],getid(num[i]));
    }
    for(int i=0;i<m;i++)
    {
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        printf("%d\n",lisan[query(1,sz,root[x-1],root[y],k)]);
    }
    return 0;
}

/**************************************************/
/**             Copyright Notice                 **/
/**  writer: wurong                              **/
/**  school: nyist                               **/
/**  blog  : http://blog.csdn.net/wr_technology  **/
/**************************************************/