题目链接
给定长度为 N 的整数序列 A, 下标为 1∼N 。
现在要执行 M 次操作,其中第 i 次操作为给出三个整数 li,ri,ki ,求 A[li],A[li+1],…,A[ri] (即 A 的下标区间 [li,ri] )中第 ki 小的数是多少。
输入格式
第一行包含两个整数 N 和 M 。
第二行包含 N 个整数,表示整数序列 A∘
接下来 M 行,每行包含三个整数 li,ri,ki ,用以描述第 i 次操作。
输出格式
对于每次操作输出一个结果,表示在该次操作中,第 k 小的数的数值。
每个结果占一行。
数据范围
N≤105,M≤104,|A[i]|≤109
输入样例:
输出样例:
解题思路
可持久化权值线段树,主席树
可持久化权值线段树的关键在于记录历史版本信息,而线段树如果不增加数的话结构是不变的,针对权值而不是下标建树,每插入一个数就复制上一个版本的信息,由于树的结构不变,可以直接复制上一个版本的根节点信息
本题值域过大,可以先离散化,树上的节点维护权值区间 [l,r] 中出现的次数,单单考虑 [1,r] 的情况,直接在以 root[r] 为根节点的线段树上求解即可,具体求解:考虑总体范围 [1,n],如果 [1,mid] 出现的个数不小于 k,说明在左半边,即位于左子树,反之位于右子树。分别考虑以 root[L−1] 和 root[R] 为根节点的线段树,考虑他们此时某个节点维护的某个某个区间 [l,r],线段树维护的是在 [l,r] 出现的数的个数,两者相减即为下标区间 [L,R] 内 [l,r] 出现的数的个数,据此分治即可
- 时间复杂度:O((n+m)×logn)
代码
#include <bits/stdc++.h>
#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__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!