萌新一枚,借鉴了许多大佬而总结下的笔记,可能有瑕疵或者错误,望指正。
1可以存储[0,n]次添加后的所有状态的线段树
2可以用于比如查找某个区间的第k大的值
3在每次修改时,不是创建一棵新的树,而是在原有树的基础上创建出一条新的链。
代码实现
1树的存储
const int maxn=1e5+10;
struct node{
int l,r,sum;
}tree[maxn<<5];
int tot;//节点编号
int rak[maxn];//用于存储离散化
int root[maxn];//记录每次修改的树的根节点
2离散化
struct data{
int id,num;
friend bool operator <(const data&a,const data&b)
{
return a.num<b.num;//按照数的大小从小到大排序
}
}a[maxn];//用于离散化
3构建一个空的树
int build(int l,int r,int p)//建立一个空的树
{
p=++tot;//创建一个新的节点
if(l==r)
return p;
int mid=(l+r)>>1;
tree[p].l=build(l,mid,tree[p].l);//构建左儿子并连接
tree[p].r=build(mid+1,r,tree[p].r);
return p;
}
4修改更新操作
int update(int l,int r,int x,int p)
{
tree[++tot]=tree[p];//复制原先节点的数据
p=tot;
tree[p].sum++;//加入了新的数字
if(l==r)
return p;
int mid=(l+r)>>1;
if(x<=mid)
tree[p].l=update(l,mid,x,tree[p].l);//如果添加的数在左边,就在分裂出一条新的链
else
tree[p].r=update(mid+1,r,x,tree[p].r);
return p;
}
5寻找区间[l,r]第k小的数
int kth_less(int u,int v,int l,int r,int k)//寻找第k小的数
{
if(l==r)
return l;
int ls=tree[tree[v].l].sum-tree[tree[u].l].sum;//在第r次添加的树左儿子减去在第l次添加的树的左儿子,就为区间[l,r]的右儿子数的数量
//类似于前缀和相减得到区间和
int mid=(r+l)>>1;
if(k<=ls)//如果左边的数的数量大于k
return kth_less(tree[u].l,tree[v].l,l,mid,k);//遍历左儿子
else
return kth_less(tree[u].r,tree[v].r,mid+1,r,k-ls);//在右儿子寻找第k-ls小的数
}
6寻找区间[l,r]第大小的数
int kth_greater(int u,int v,int l,int r,int k)//寻找第k大的数,和前面反着来
{
if(l==r)
return l;
int rs=tree[tree[v].r].sum-tree[tree[u].r].sum;//在第r次添加的树右儿子减去在第l次添加的树的右儿子,就为区间[l,r]的右儿子数的数量
int mid=(r+l)>>1;
if(k<=rs)//如果右边的数的数量大于k
return kth_greater(tree[u].r,tree[v].r,mid+1,r,k);//遍历右儿子
else
return kth_greater(tree[u].l,tree[v].l,l,mid,k-rs);//在左儿子寻找第k-rs大的数
}
7主函数应用,n个数,q次询问,询问[l,r]中第k大的和第k小的数
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
a[i].id=i;
cin>>a[i].num;
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
rak[a[i].id]=i;//离散化,rak,为离散化后的数组
root[0]=build(1,n,1);//建立空树
for(int i=1;i<=n;i++)
{
root[i]=update(1,n,rak[i],root[i-1]);//依次添加数字
}
while(q--)//q次询问
{
int l,r,k;
cin>>l>>r>>k;
cout<<"第"<<k<<"小:"<<a[kth_less(root[l-1],root[r],1,n,k)].num<<'\n';
cout<<"第"<<k<<"大:"<<a[kth_greater(root[l-1],root[r],1,n,k)].num<<'\n';
}
return 0;
}