主席树

主席树的本质就是线段树,但是比线段的优势是可以保留历史状态,也就是后面建立的树通过减去前面的树——节点相减产生一颗“新”的线段树然后解决区间第k大的问题。


每次建立线段树其实只是添加了logn个节点然后连到已有的线段树,通过root数组保持最上面的根节点的数组下标

然后就是主席树的每个节点维护的当前区间有多少个数。

#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int root[maxn],a[maxn],x,y,r,l,k,cnt;//root数组表示新建的树的根节点的下标
vector<int>v;
struct node
{
    int r,l,sum;
}t[maxn*40];
int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void Update(int l,int r,int &pre,int pos)//pre表示前一颗树节点的下标 使用应用进行实时更新
{
    int rt=cnt++;
    //cout<<"rt "<<rt<<" cnt="<<cnt<<endl;
    t[rt]=t[pre],t[rt].sum++,pre=rt;
    if(r==l)
     return ;
    int mid=(r+l)>>1;
    if(mid>=pos)
    Update(l,mid ,t[pre].l,pos);
    else
    Update(mid+1,r,t[pre].r,pos);
}
int Queuer(int l,int r,int x,int y,int k)
{


   if(l==r)
        return r;
    int sum=t[t[y].l].sum-t[t[x].l].sum;
    int mid=(r+l)>>1;
    if(sum>=k)
       return Queuer(l,mid,t[x].l,t[y].l,k);
    else
        return Queuer(mid+1,r,t[x].r,t[y].r,k-sum);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int m,n;
    cin>>n>>m;
    for(int i=1; i<=n; i++)
        cin>>a[i],v.push_back(a[i]);
    sort(v.begin(),v.end()), v.erase(unique( v.begin(),v.end()),v.end());//离散化黑科技
    root[0]=0,t[0].r=t[0].l=t[0].sum=0;
    cnt=1;//cnt 为0将覆盖root[0]出错
    for(int i=1; i<=n; i++)
    {
        root[i]=root[i-1];
        Update(1,n,root[i],getid(a[i]));//所谓的更新其实就是添加节点建树
    }
    while(m--)
    {
         cin>>x>>y>>k;
         cout<<v[Queuer(1,n,root[x-1],root[y],k)-1]<<endl;
    }


    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值