區間第k大(靜態)——主席樹
阿新 • • 發佈:2018-12-09
Description
給定一個長度為n的序列,m個詢問,每個詢問的形式為:L,r,k表示在[L,r]間中的第k大元素。
Input
第1行:2個數,n,m表示序列的長度和詢問的個數
第2行:n個數,表示n個數的大小
第3-m+2行:每行3個數,L,r,k表示詢問在[L,r]區間內第k小的元素
Output
對於每個詢問,輸出答案。
Sample Input
7 2 1 5 2 6 3 7 4 1 5 3 2 7 1
Sample Output
3 2
Hint
對於100%的資料,n<=100000, m<=100000,1<=L<=r<=n, 1<=k<=r-L+1
主席樹入門級題目,我們維護的是權值線段樹。
rt[x]表示第x個數當根的前x個線段樹的根,於是就有了字首和的性質,那麼我們查詢[l,r]的時候將rt[r]-rt[l-1]即可計算出來。
發現修改操作(單點修改)最多logn個點受影響,於是我們可以共用很多資訊,動態開點修改鏈上的。(這道題太水。。。感覺沒有突出主席樹的強大功能(雖然難了我也不會))。
值得注意的是主席樹的空間複雜度略高(貌似和樹套樹差不多啊。。。),開線段樹的時候記憶體記得開大一點,經過剛才的理性分析,似乎也是nlogn級別的空間複雜度?
#include<bits/stdc++.h> using namespace std; const int Maxn=100005; struct SegMent{ struct Node{ int ls,rs,sum; }t[Maxn*8]; int cnt,rt[Maxn]; inline void modify(int val,int &x,int o,int l,int r){ t[x=++cnt]=t[o]; ++t[x].sum; if(l==r)return ; int mid=l+r>>1; if(val<=mid)modify(val,t[x].ls,t[o].ls,l,mid); else modify(val,t[x].rs,t[o].rs,mid+1,r); } inline int query(int kth,int lt,int rt,int l,int r){ int sum=t[t[rt].ls].sum-t[t[lt].ls].sum; int mid=l+r>>1; if(l==r)return mid; if(sum>=kth)return query(kth,t[lt].ls,t[rt].ls,l,mid); else return query(kth-sum,t[lt].rs,t[rt].rs,mid+1,r); } }seg; int main(){ int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ int a;scanf("%d",&a); seg.modify(a,seg.rt[i],seg.rt[i-1],0,1<<30); } for(int i=1;i<=m;++i){ int l,r,k;scanf("%d%d%d",&l,&r,&k); printf("%d\n",seg.query(k,seg.rt[l-1],seg.rt[r],0,1<<30)); } return 0; }