主席树学习笔记

终于了解了最简单主席树- -
写代码犯了很SB的错误,调了半小时才看出来,难受


我学到的主席树是个支持查询历史版本的权值线段树
如何支持查询历史版本呢?
很容易想到每次更新的时候建一颗新的线段树
但显然空间爆炸
所以我们只需要新开那些需要更新的节点,不需要更新的节点直接拉到上一个版本即可
所以查[l,r]的k大值只需要查询第r个版本减去第l-1个版本的数据就行了
通常需要离散化一下
代码出乎意料的短。。。但是犯了个智障错误调了半个小时。。。
果然90%的时间都花在调试sb错误上

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

#define For(i,l,r) for(int i=l;i<=r;++i)
#define MAXN (200010)
#define mid ((l+r)>>1)

using namespace std;
int read()
{
     char c;
     bool t=0;
     int a=0;
     while((c=getchar())==' '||c=='\n'||c=='\r');
     if(c=='-')
     {
        t=1;
        c=getchar();
     }
     while(isdigit(c))
     {
        a*=10;
        a+=(c-'0');
        c=getchar();
     }
     return a*(t?-1:1);
}
int ls[MAXN<<5],rs[MAXN<<5],sum[MAXN<<5];
int root[MAXN],cnt;
int n,m,a[MAXN],hash[MAXN],len,ans;
int getHash(const int &x)//离散化
{
    return lower_bound(hash+1,hash+len+1,x)-hash;
}
void build(int l,int r,int x)
{
    if(l==r)
     return;
    ls[x]=++cnt;
    build(l,mid,cnt);
    rs[x]=++cnt;
    build(mid+1,r,cnt);
    //printf("[l:%d r:%d x:%d ls:%d rs:%d]\n",l,r,x,ls[x],rs[x]);
}
void update(int w,int l,int r,int x,int his)//要修改的值 左端 右端 要修改的点 要修改的点的前一个版本 
{
    if(l==r)
    {sum[x]=sum[his]+1;return;}//AAAA
    ls[x]=ls[his];rs[x]=rs[his];sum[x]=sum[his]+1;
    if(w<=mid)
    {
        ls[x]=++cnt;
        update(w,l,mid,cnt,ls[his]);
    }
    else
    {
        rs[x]=++cnt;
        update(w,mid+1,r,cnt,rs[his]);
    }
    //sum[x]=sum[ls[x]]+sum[rs[x]];
}
void ask(int l,int r,int rx,int lx,int k)//当前点左 当前点右 当前右点 当前左点 第k小 
{
    if(l==r)
    {
        ans=hash[l];
        return;
    }
    if(k<=sum[ls[rx]]-sum[ls[lx]])//在左边 
     ask(l,mid,ls[rx],ls[lx],k);
    else//在右边 
     ask(mid+1,r,rs[rx],rs[lx],k-sum[ls[rx]]+sum[ls[lx]]);
}
int main()
{
    int tx,ty,tk;
    n=read();m=read();
    For(i,1,n)
     a[i]=read();
    memcpy(hash,a,sizeof a);
    sort(hash+1,hash+n+1);
    len=unique(hash+1,hash+n+1)-hash-1;
    root[0]=++cnt;
    build(1,len,cnt);
    For(i,1,n)
    {
        root[i]=++cnt;
        update(getHash(a[i]),1,len,cnt,root[i-1]);
    }
    For(i,1,m)
    {
        tx=read();ty=read();tk=read();
        if(tx==ty)
        {printf("%d\n",a[tx]);continue;}
        ask(1,len,root[ty],root[tx-1],tk);
        printf("%d\n",ans);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值