原谅博主太蒟。。。实在讲不明白
http://www.cnblogs.com/zyf0163/p/4749042.html(讲得很好O(∩_∩)O~)
在粘模板之前,先进行一下简单的解释:
我们先 sort(a+1,a+1+n,cmp); 这是为什么呢~
其实,我们在构建线段树时,按照排序进行添加,
rank[i]是i元素在原始队列里的位置,添加第i个,就在rank[i]的位置上++,
实际上这样就(在不知不觉中)实现了离散化,
再看一下这个
void insert(int num,int &now,int l,int r)
这里的num 就是该元素在原序列中的位置,now是它在线段树中的位置,
l是左端点,r是右端点,
在这里 now 加了&(取地址符)这也就是说,在进行递归构建线段树的过程中,
now传递的元素会改变,所以就不用特意去维护了
这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
struct node{
int rank,v;
};
node a[N];
struct node1{
int l,r,sum;
};
node1 tree[N*20];
int n,m,rank[N],tot=0,root[N],u,v,z;
void insert(int num,int &now,int l,int r) //构建线段树,&取地址符
{
tree[++tot]=tree[now];
now=tot;
tree[now].sum++; //sum记录的是:相对于这个节点,有多少个比ta大的数
if (l==r) return;
int mid=(l+r)>>1;
if (num<=mid) insert(num,tree[now].l,l,mid); //该元素在线段树的左儿子中
else insert(num,tree[now].r,mid+1,r); //该元素在线段树的右儿子中
}
int cmp(const node &a,const node &b)
{
return a.v<b.v; //从小到大排序
}
int q(int i,int j,int k,int l,int r) //[i,j]是询问区间
{
if (l==r) return l;
int ans=tree[tree[j].l].sum-tree[tree[i].l].sum;
int mid=(l+r)>>1;
if (k<=ans) q(tree[i].l,tree[j].l,k,l,mid);
else q(tree[i].r,tree[j].r,k-ans,mid+1,r); //k-ans!!!
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i].v);
a[i].rank=i;
}
sort(a+1,a+1+n,cmp); //排序后相当于建好了一颗小根堆
for (int i=1;i<=n;i++)
rank[a[i].rank]=i; //rank[i] 原序列中第i个排第几
for (int i=1;i<=n;i++)
{
root[i]=root[i-1]; //记录一下根
insert(rank[i],root[i],1,n);
}
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&z);
printf("%d
",a[q(root[u-1],root[v],z,1,n)].v);
}
return 0;
}