#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
struct Parti_tree{
#define MID(x,y) ( ( x + y ) >> 1 )
#define L(x) ( x << 1 )
#define R(x) ( x << 1 | 1 )
static const int MAX = 100010;
struct Tnode{
int l,r;
inline int len() { return r - l;}
inline int mid() { return MID(l,r);}
inline bool in(int ll,int rr) { return l >= ll && r <= rr; }
inline void lr(int ll,int rr){ l = ll; r = rr;}
};
Tnode node[MAX<<2];
int Left[20][MAX];//保存每个元素前面的元素进入左子树的个数(不包括自己)
int seg[20][MAX];//保存每层的元素
int sa[MAX];//保存排序好的数组,范围:1 - n
void init(){
memset(Left,0,sizeof(Left));
memset(node,0,sizeof(node));
}
void build(int s,int t){
sort(sa+1,sa+t+1);
Parti_build(1,s,t,1);
}
int find(int s,int t,int k){
return find_rank(1,s,t,1,k);
}
void Parti_build(int t,int l,int r,int d)
{//t节点编号,l,r节点控制的范围,d层数
node[t].lr(l, r);
if( node[t].len() == 0 ) return ;
int mid = MID(l, r);
int lsame = mid - l + 1;
//初始化能进入左子树且和中间元素相等的值
for(int i=l; i<=r; i++)
if(seg[d][i] < sa[mid])
lsame--;
int lpos = l;//左子树开始位置
int rpos = mid+1;//右子树开始位置
int same = 0;//进入左子树且和mid相等的元素的数目
for(int i=l; i<=r; i++)
{
if( i == l )
Left[d][i] = 0;
else
Left[d][i] = Left[d][i-1];
if( seg[d][i] < sa[mid] ){
Left[d][i]++;//进入左子树,更新left
seg[d+1][lpos++] = seg[d][i];
}
else if(seg[d][i] > sa[mid])//进入右子树
seg[d+1][rpos++] = seg[d][i];
if(seg[d][i] == sa[mid])
if(same < lsame){
same++;//还剩位置,进入左子树,更新left
Left[d][i]++;
seg[d+1][lpos++] = seg[d][i];
}
else//否则进入右子树
seg[d+1][rpos++] = seg[d][i];
}
Parti_build(L(t), l, mid, d+1);
Parti_build(R(t), mid+1, r, d+1);
}
int find_rank(int t,int L,int R,int d,int k)
{
if( node[t].len() == 0 )
return seg[d][L];//区间长度为1,找到元素
int ss;//ss表示从[l,L-1]有多少个小于sa[mid]的数被分到左边
if( L == node[t].l )
ss = 0;
else
ss = Left[d][L-1];
int s = Left[d][R] - ss;//s表示区间[L,R]有多少个小于sa[mid]的数被分到左边
if(s >= k)//进入左子树的个数大于k,那第k小在左子树
return find_rank(L(t), node[t].l+ss, node[t].l+ss+s-1, d+1, k);
else{
int mid = node[t].mid();
int bb = L - node[t].l - ss;//[l,L-1]区间内有多少个数进入右子树
int b = R - L + 1 - s;//[L,R]区间有多少个进入右子树
return find_rank(R(t), mid+bb+1, mid+bb+b,d+1,k-s);
}
}
};
Parti_tree t;
int main()
{
int n,m,x,y,k;
while( ~scanf("%d%d",&n,&m) )
{
t.init();
for(int i=1; i<=n; i++)
{
scanf("%d",&t.sa[i]);
t.seg[1][i] = t.sa[i];
}
t.build(1,n);
while( m-- )
{
scanf("%d%d%d",&x,&y,&k);
int ans = t.find(x, y, k);
printf("%d\n",ans);
}
}
return 0;
}
题意:给出长度为n的序列。给出m个操作,每个操作是求出区间[l,r]中第k小的数字。
思路:标准的划分树,直接上代码吧。
代码如下: