若想要深刻學習主席樹,傳送門。html
Description:c++
給定數列 \(\{a_n\}\) ,求閉區間 \([l,r]\) 的第 \(k\) 小的數。數組
Method:學習
先對數據進行離散化,而後按照權值創建線段樹。ui
若要尋找 \([1,p]\) 的第 \(k\) 小,則從根節點開始處理。定義\(Son_{left}\) 表示左兒子的集合,\(Son_{right}\) 表示右兒子的集合。若 \(|Son_{left}|\ge k\) 時,說明第\(k\)小的數在左子樹中,以左兒子爲新的根向下遞歸更新,尋找左子樹中第 \(k\) 小的數;反之,說明第\(k\)小的數在右子樹中,以左兒子爲新的根向下遞歸更新,尋找左子樹中第 \(k-|Son_{left}|\) 小的數。spa
拓展一下,咱們先預處理建樹,獲得 \(n+1\) 個版本的線段樹(包括初始的線段樹),編號爲 \(0 \sim n\) 。code
前文提到過,主席樹知足前綴和查詢的思想,故咱們要求 \([l,r]\) 的第 \(k\) 小值,便可用sum[r]-sum[l-1]
。htm
Code:blog
#include<bits/stdc++.h> #define int long long #define Maxn 200010 using namespace std; inline void read(int &x) { int f=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} x*=f; } int n,m; struct Segtree { int ls,rs,sum; }tree[Maxn<<5]; int rt[Maxn]; int a[Maxn],ins[Maxn]; int len,tot=0; inline void Init(){tot=0;} inline int getid(const int &x) { return lower_bound(ins+1,ins+len+1,x)-ins; } inline void pushup(int rt) { tree[rt].sum=tree[tree[rt].ls].sum+tree[tree[rt].rs].sum; } inline int build(int l,int r) { int rt=++tot; if(l==r) { tree[rt].sum=0; return rt; } int mid=(l+r)/2; tree[rt].ls=build(l,mid); tree[rt].rs=build(mid+1,r); pushup(rt); return rt; } int update(int k,int l,int r,int root,int val) { int rt=++tot; tree[rt]=tree[root]; if(l==k&&r==k) { tree[rt].sum+=val; return rt; } int mid=(l+r)/2; if(k<=mid) tree[rt].ls=update(k,l,mid,tree[rt].ls,val); else tree[rt].rs=update(k,mid+1,r,tree[rt].rs,val); pushup(rt); return rt; } int query(int u,int v,int l,int r,int k) { if(l==r) return l; int mid=(l+r)/2,x=tree[tree[v].ls].sum-tree[tree[u].ls].sum; if(k<=x) return query(tree[u].ls,tree[v].ls,l,mid,k); else return query(tree[u].rs,tree[v].rs,mid+1,r,k-x); } signed main() { Init(); read(n),read(m); for(int i=1;i<=n;i++) { read(a[i]); } memcpy(ins,a,sizeof(ins)); sort(ins+1,ins+n+1); len=unique(ins+1,ins+n+1)-ins-1; rt[0]=build(1,len); for(int i=1;i<=n;i++) { rt[i]=update(getid(a[i]),1,len,rt[i-1],1); } while(m--) { int l,r,k; read(l),read(r),read(k); printf("%lld\n",ins[query(rt[l-1],rt[r],1,len,k)]); } return 0; }
Warning:遞歸
ls[]
,rs[]
,sum[]
等數組都要乘上 \(2^5\) 。lower_bound
時,是最後減去0開頭的地址,而不是1開頭的地址。(便是lower_bound(ins+1,ins+n+1,x)-ins
,而不是lower_bound(ins+1,ins+n+1,x)-ins-1
)