题目链接:poj_2104 K-th Number
首先我们要知道Tree中所存储的东西,Tree.sum是它所代区间中有几个数,Tree.l 是它的左区间所在结点的下标,Tree.r 是它的右区间所在结点的下标。
感觉代码有几个神奇之处:
1. root[i] = root[i-1]; 直接把 [1,i-1] 这课线段树上存储的东西 给了 [1,i] 这颗线段树 ,
相当于第i颗线段树只需要特指 logn个结点来更新被 第i个数改变的结点,其他结点由于没有更新,所以可以沿用第i-1 颗线段树上的内容。
2. Tree[0].l=Tree[0].r=Tree[0].sum=root[0]=0; 创建了第0颗线段树,和第0个结点,这样所有的没有存储内容的结点都可以用第0个结点所表示。
3. void update(int x,int &fre,int l,int r)
Tree[++cnt] = Tree[fre];
fre = cnt ;
update(x,Tree[fre].l,l,m);
#include<cstdio>
#include<algorithm>
#define maxn 100100
using namespace std;
struct nn{
int value,id;
}num[maxn];
bool cmp(nn x,nn y){
return x.value<y.value;
}
struct node{
int l,r,sum;
}Tree[maxn*20];
int root[maxn];
int a[maxn],cnt;
void update(int x,int &fre,int l,int r){
Tree[++cnt] = Tree[fre];
fre = cnt ;
Tree[fre].sum++;
if (l==r) return ;
int m = (l+r)>>1;
if (x<=m){
update(x,Tree[fre].l,l,m);
}else{
update(x,Tree[fre].r,m+1,r);
}
}
int query(int a,int b,int k,int l,int r){
if (l==r) return l;
int num_sum = Tree[Tree[b].l].sum - Tree[Tree[a].l].sum;
int m = (l+r)>>1;
// printf("a=%d b=%d k=%d l=%d r=%d num_sum=%d m=%d \n",a,b,k,l,r,num_sum,m);
if (num_sum<k){
return query(Tree[a].r,Tree[b].r,k-num_sum,m+1,r);
}else{
return query(Tree[a].l,Tree[b].l,k,l,m);
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&num[i].value);
num[i].id = i;
}
sort(num+1,num+1+n,cmp);
for (int i=1;i<=n;i++){
a[num[i].id] = i;
}
cnt=Tree[0].l=Tree[0].r=Tree[0].sum=root[0]=0;
for (int i=1;i<=n;i++){
root[i] = root[i-1];
update(a[i],root[i],1,n);
}
// for (int i=1;i<=n;i++){
// printf("root[%d] = %d\n",i,root[i]);
// }
// for (int i=0;i<=cnt;i++){
// printf("i = %d, Tree[].l=%d, Tree[].r=%d ,Tree[].sum=%d\n",i,Tree[i].l,Tree[i].r,Tree[i].sum);
// }
for (int i=1;i<=m;i++){
int aa,bb,kk;
scanf("%d%d%d",&aa,&bb,&kk);
printf("%d\n",num[query(root[aa-1],root[bb],kk,1,n)].value);
}
return 0;
}