第一道主席树。。说下理解。首先把数离散化成1~n,然后建立n+1棵线段树,第i棵树存的是前i个数加入后,各个区间有多少个数。第i棵树,实际上只有log(n)个节点和第i-1棵树不同,利用之前的节点,可以大大节省空间。
对于查询lr,这个区间的内容实际上是r和l-1两棵树的差。这样就可以在log(n)的时间内得到区间第k小。
#include <algorithm> #include <map> #include <iostream> #include <stdio.h> using namespace std; const int maxn = 100010; int n,m; int a[maxn]; int val[maxn*22]; int pl[maxn*22]; int pr[maxn*22]; int rts[maxn]; int tot; void build(int root,int l,int r){ val[root]=0; if(l==r)return; pl[root]=tot++; pr[root]=tot++; int mid = (l+r)>>1; build(pl[root],l,mid); build(pr[root],mid+1,r); } void insert(int root,int preRoot,int l,int r,int v){ val[root]=val[preRoot]+1; while(l<r){ int mid=(l+r)>>1; if(v<=mid){ pl[root]=tot++; pr[root]=pr[preRoot]; root = pl[root]; preRoot = pl[preRoot]; r=mid; }else{ pl[root]=pl[preRoot]; pr[root]=tot++; root = pr[root]; preRoot = pr[preRoot]; l=mid+1; } val[root]=val[preRoot]+1; } } int query(int l,int r,int k){ int tl=1; int tr=n; int rtl=rts[l-1]; int rtr=rts[r]; while(tl<tr){ //check left int mid = (tl+tr)>>1; if( val[pl[rtr]] - val[pl[rtl]] >=k ){ tr = mid; rtl = pl[rtl]; rtr = pl[rtr]; }else{ k-= (val[pl[rtr]]-val[pl[rtl]]); tl=mid+1; rtl = pr[rtl]; rtr = pr[rtr]; } } return tr; } map<int,int> mp; int _mp[maxn]; int main(){ while(cin>>n>>m){ tot=0; mp.clear(); build(0,1,n); rts[0] = 0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); mp[a[i]] = 0; } map<int,int>::iterator it; int rank=0; for(it=mp.begin();it!=mp.end();it++){ rank++; it->second = rank; _mp[rank] = it->first; } for(int i=1;i<=n;i++){ rts[i]=tot++; insert(rts[i],rts[i-1],1,n,mp[a[i]]); } for(int i=1;i<=m;i++){ int l,r,k; scanf("%d%d%d",&l,&r,&k); int ans = query(l,r,k); ans = _mp[ans]; printf("%d\n",ans); } } return 0; }