主席樹學習--入門
阿新 • • 發佈:2019-02-10
本篇文章,講講主席樹入門,以及區間第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 **/ /**************************************************/