255. 第K小数

题目链接

255. 第K小数

给定长度为 N 的整数序列 A, 下标为 1N
现在要执行 M 次操作,其中第 i 次操作为给出三个整数 li,ri,ki ,求 A[li],A[li+1],,A[ri] (即 A 的下标区间 [li,ri] )中第 ki 小的数是多少。

输入格式

第一行包含两个整数 NM
第二行包含 N 个整数,表示整数序列 A
接下来 M 行,每行包含三个整数 li,ri,ki ,用以描述第 i 次操作。

输出格式

对于每次操作输出一个结果,表示在该次操作中,第 k 小的数的数值。
每个结果占一行。

数据范围

N105,M104,|A[i]|109

输入样例:

7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3

输出样例:

5 6 3

解题思路

可持久化权值线段树,主席树

可持久化权值线段树的关键在于记录历史版本信息,而线段树如果不增加数的话结构是不变的,针对权值而不是下标建树,每插入一个数就复制上一个版本的信息,由于树的结构不变,可以直接复制上一个版本的根节点信息

本题值域过大,可以先离散化,树上的节点维护权值区间 [l,r] 中出现的次数,单单考虑 [1,r] 的情况,直接在以 root[r] 为根节点的线段树上求解即可,具体求解:考虑总体范围 [1,n],如果 [1,mid] 出现的个数不小于 k,说明在左半边,即位于左子树,反之位于右子树。分别考虑以 root[L1]root[R] 为根节点的线段树,考虑他们此时某个节点维护的某个某个区间 [l,r],线段树维护的是在 [l,r] 出现的数的个数,两者相减即为下标区间 [L,R][l,r] 出现的数的个数,据此分治即可

  • 时间复杂度:O((n+m)×logn)

代码

// Problem: 第K小数 // Contest: AcWing // URL: https://www.acwing.com/problem/content/257/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e5+5; int n,m,a[N],root[N],idx; vector<int> b; struct T { int l,r; int cnt; }tr[N*4+17*N]; int find(int x) { return lower_bound(b.begin(),b.end(),x)-b.begin(); } int build(int l,int r) { int p=++idx; if(l==r)return p; int mid=l+r>>1; tr[p].l=build(l,mid),tr[p].r=build(mid+1,r); return p; } int insert(int p,int l,int r,int x) { int q=++idx; tr[q]=tr[p]; if(l==r) { tr[q].cnt++; return q; } int mid=l+r>>1; if(x<=mid)tr[q].l=insert(tr[p].l,l,mid,x); else tr[q].r=insert(tr[p].r,mid+1,r,x); tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt; return q; } int ask(int q,int p,int l,int r,int k) { if(l==r)return l; int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt; int mid=l+r>>1; if(k<=cnt)return ask(tr[q].l,tr[p].l,l,mid,k); else return ask(tr[q].r,tr[p].r,mid+1,r,k-cnt); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b.pb(a[i]); } sort(b.begin(),b.end()); b.erase(unique(b.begin(),b.end()),b.end()); root[0]=build(0,b.size()-1); for(int i=1;i<=n;i++) root[i]=insert(root[i-1],0,b.size()-1,find(a[i])); while(m--) { int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",b[ask(root[r],root[l-1],0,b.size()-1,k)]); } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16155636.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示