http://acm.hdu.edu.cn/showproblem.php?pid=2665

题目大意
静态区间第 k 小数
给定一个长度为 n 的序列 A,你需要回答 q 次询问。每次询问给定
l; r; k,你需要回答 A 序列 [l; r] 区间内第 k 小的数字。
n; q ≤ 105; 1 ≤ k ≤ r − l + 1; 1 ≤ l ≤ r ≤ n; jAij ≤ 109
思路
- 我们对于每个前缀 i,求出包含 A1; A2; ; Ai 所有元素的权值线段树。
- 询问时只需要在线段树上二分最大的位置 v,使得 Al; Al+1; Ar 区间
内 ≤ v 的个数 ≤ k 即可。 - 时间复杂度均为 O((n + q) log n),空间复杂度为 O(n log n)。
solution
- 对于每一个时刻,都建一颗线段树,只不过我们可以充分运用之前已有的线段树,我们发现每加一个数,都仅仅只修改了logn个点的信息。所以“ls[y]=ls[x];rs[y]=rs[x]”.
- 查询的时候,因为每一个线段树都是同构的,所以对应节点减一下就是新线段树,在此线段树二分找kth即可
- t[i]的意义:以i为根的树的编号(tot)。
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read() {
int x=0,f=1;char ch=' ';
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=100100;
int a[N],b[N],t[N];
int tot=0;
int ls[N<<5],rs[N<<5],sum[N<<5];
void build(int &y,int l,int r)
{
y=++tot;sum[y]=0;
if(l==r) return ;
int mid=l+r>>1;
build(ls[y],l,mid);
build(rs[y],mid+1,r);
}
void modify(int &y,int x,int l,int r,int p)
{
y=++tot;sum[y]=sum[x]+1;
if(l==r) return ;
int mid=(l+r)>>1;ls[y]=ls[x];rs[y]=rs[x];
if(p<=mid) modify(ls[y],ls[x],l,mid,p);
else modify(rs[y],rs[x],mid+1,r,p);
}
int query(int y,int x,int l,int r,int k)
{
if(l==r) return l;
int s=sum[ls[y]]-sum[ls[x]],mid=(l+r)>>1;
if(k<=s) return query(ls[y],ls[x],l,mid,k);
else return query(rs[y],rs[x],mid+1,r,k-s);
}
int main()
{
int T;
T=read();
while(T--)
{
tot=0;
int n,m;
n=read();m=read();
int i,j;
for(i=1;i<=n;i++)
{
a[i]=read();
b[i]=a[i];
}
sort(b+1,b+1+n);
int d;
d=unique(b+1,b+1+n)-b-1;
build(t[0],1,d);
for(i=1;i<=n;i++)
{
int x=lower_bound(b+1,b+1+d,a[i])-b;
modify(t[i],t[i-1],1,d,x);
}
for(i=1;i<=m;i++)
{
int l,r,k;
l=read();r=read();k=read();
int pos=query(t[r],t[l-1],1,d,k);
cout<<b[pos]<<endl;
}
}
return 0;
}

402

被折叠的 条评论
为什么被折叠?



