考察:主席树
思路:
利用二分的思想,即在主席树上二分,详细参考代码,主要记板子.
1 #include <iostream> 2 #include <cstring> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 const int N = 100010; 7 int n,m,root[N],idx,a[N]; 8 vector<int> v; 9 struct Node{ 10 int l,r,cnt; 11 Node operator=(const Node& x){ 12 this->l = x.l; 13 this->r = x.r; 14 this->cnt = x.cnt; 15 return *this; 16 } 17 }tr[N*21]; 18 int find(int x) 19 { 20 return lower_bound(v.begin(),v.end(),x)-v.begin(); 21 } 22 int build(int l,int r) 23 { 24 int p = ++idx;//构造主席树结点下标 25 if(l==r) return p; 26 int mid = l+r>>1; 27 tr[p].l = build(l,mid);//l代表左子树结点下标 28 tr[p].r = build(mid+1,r);//l,r不是区间范围 29 tr[p].cnt = 0; 30 return p; 31 } 32 int insert(int l,int r,int last,int val) 33 { 34 int p = ++idx;//建立新树,只需要上个版本的对应下标 35 tr[p] = tr[last]; 36 if(l==r) {tr[p].cnt++;return p;}//返回新改变的点 37 int mid = l+r>>1; 38 if(val<=mid) tr[p].l = insert(l,mid,tr[last].l,val); 39 else tr[p].r = insert(mid+1,r,tr[last].r,val); 40 tr[p].cnt = tr[tr[p].l].cnt+tr[tr[p].r].cnt;//更新 41 return p; 42 } 43 int ask(int now,int last,int k,int l,int r)//询问[l,r]区间第k小 44 {//求[l,mid] 区间有x个数.if x<=k 在[l,mid]里找,否则[mid+1,r]找 45 if(l==r) return v[l]; 46 int mid = l+r>>1; 47 int lcnt = tr[tr[now].l].cnt-tr[tr[last].l].cnt;//tr[l]代表[l,mid]区间 48 if(k<=lcnt) return ask(tr[now].l,tr[last].l,k,l,mid); 49 else return ask(tr[now].r,tr[last].r,k-lcnt,mid+1,r); 50 } 51 int main() 52 { 53 scanf("%d%d",&n,&m); 54 for(int i=1;i<=n;i++) 55 { 56 scanf("%d",&a[i]); 57 v.push_back(a[i]); 58 } 59 sort(v.begin(),v.end()); 60 v.erase(unique(v.begin(),v.end()),v.end()); 61 root[0] = build(0,v.size()-1); 62 for(int i=1;i<=n;i++) 63 root[i] = insert(0,v.size()-1,root[i-1],find(a[i])); 64 while(m--) 65 { 66 int l,r,k; 67 scanf("%d%d%d",&l,&r,&k); 68 printf("%d\n",ask(root[r],root[l-1],k,0,v.size()-1)); 69 } 70 return 0; 71 }