K-th Number Poj - 2104 主席树
题意
给你n数字,然后有m次询问,询问一段区间内的第k小的数。
解题思路
这个题是限时训练做的题,我不会,看到这个题我开始是拒绝的,虽然题意清晰简单,但是真的不会。限时结束后,学长说这个题是简单的主席树的入门题,我没学过啊。
如果你也没有学过的话,建议看我的另一篇博客,上面有自己的总结和一些博客推荐,就不用自己一个一个找了,点我进去。
哦, 这个题是主席树的模板题。
代码实现
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int maxn=1e5+7; int root[maxn], a[maxn], x, y, k; int n, m, cnt; struct node{ int l, r, sum; //左子树的编号,右子树的编号,区间内的标记的数的个数 }t[maxn*40]; vector<int> v; int getid(int x) //需要进行离散化,因为主席树叶子端点上就是一个数。 { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; //这里用vector进行的离散化,比较简单 } void update(int l, int r, int &x, int y, int pos) //注意参数中的l和r是区间范围,和node里面的l和r不一样,node里面的l和r表示左右子树的编号 //这里相当于建树+更新了,有的博客还单独写了一个build函数,然后再进行update,都行。 { t[++cnt]=t[y]; t[cnt].sum++; x=cnt;//这里是把新节点的编号记录下来 if(l==r) return ; int mid=(l+r)>>1; if(pos<=mid)//如果需要添加的编号小于mid,就向左子树走 update(l, mid, t[x].l, t[y].l, pos); else //否则就是往右子树走 update(mid+1, r, t[x].r, t[y].r, pos); } int query(int l, int r, int x, int y, int k) //这里的l和r也是区间范围,x是前一个线段树 { if(l==r) return l; int mid=(l+r)>>1; int sum=t[t[y].l].sum - t[t[x].l].sum; if(k<=sum) 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() { scanf("%d%d", &n, &m); for(int i=1; i<=n; i++) { scanf("%d", &a[i]); v.push_back(a[i]); } sort(v.begin(), v.end()); //先排序,才能去重+离散化 v.erase( unique( v.begin(), v.end() ) , v.end() );//去重 for(int i=1; i<=n; i++) { update(1, n, root[i], root[i-1], getid(a[i]) ); } for(int i=1; i<=m; i++) { scanf("%d%d%d", &x, &y, &k); printf("%d\n", v[ query(1, n, root[x-1], root[y], k) - 1]); } return 0; }