• 洛谷P3834题解


    若想要深入学习主席树,传送门


    Description:

    给定数列 ({a_n}) ,求闭区间 ([l,r]) 的第 (k) 小的数。

    Method:

    先对数据进行离散化,然后按照权值建立线段树。

    若要寻找 ([1,p]) 的第 (k) 小,则从根节点开始处理。定义(Son_{left}) 表示左儿子的集合,(Son_{right}) 表示右儿子的集合。若 (|Son_{left}|ge k) 时,说明第(k)小的数在左子树中,以左儿子为新的根向下递归更新,寻找左子树中第 (k) 小的数;反之,说明第(k)小的数在右子树中,以左儿子为新的根向下递归更新,寻找左子树中第 (k-|Son_{left}|) 小的数。

    拓展一下,我们先预处理建树,得到 (n+1) 个版本的线段树(包括初始的线段树),编号为 (0 sim n)

    前文提到过,主席树满足前缀和查询的思想,故我们要求 ([l,r]) 的第 (k) 小值,即可用sum[r]-sum[l-1]

    Code:

    #include<bits/stdc++.h>
    #define int long long 
    #define Maxn 200010
    using namespace std;
    inline void read(int &x)
    {
        int f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    int n,m;
    struct Segtree
    {
        int ls,rs,sum;
    }tree[Maxn<<5];
    int rt[Maxn];
    int a[Maxn],ins[Maxn]; 
    int len,tot=0;
    inline void Init(){tot=0;}
    inline int getid(const int &x)
    {
        return lower_bound(ins+1,ins+len+1,x)-ins;
    }
    inline void pushup(int rt)
    {
        tree[rt].sum=tree[tree[rt].ls].sum+tree[tree[rt].rs].sum;
    }
    inline int build(int l,int r)
    {
        int rt=++tot;
        if(l==r) 
        {
            tree[rt].sum=0;
            return rt;
        }
        int mid=(l+r)/2;
        tree[rt].ls=build(l,mid);
        tree[rt].rs=build(mid+1,r);
        pushup(rt);
        return rt;
    }
    int update(int k,int l,int r,int root,int val)
    {
        int rt=++tot;
        tree[rt]=tree[root];
        if(l==k&&r==k)
        {
            tree[rt].sum+=val;
            return rt;
        }
        int mid=(l+r)/2;
        if(k<=mid) tree[rt].ls=update(k,l,mid,tree[rt].ls,val);
        else tree[rt].rs=update(k,mid+1,r,tree[rt].rs,val);
        pushup(rt);
        return rt;
    }
    int query(int u,int v,int l,int r,int k)
    {
        if(l==r) return l;
        int mid=(l+r)/2,x=tree[tree[v].ls].sum-tree[tree[u].ls].sum;
        if(k<=x) return query(tree[u].ls,tree[v].ls,l,mid,k);
        else return query(tree[u].rs,tree[v].rs,mid+1,r,k-x);
    }
    signed main()
    {
        Init();
        read(n),read(m);
        for(int i=1;i<=n;i++)
        {
            read(a[i]);
        }
        memcpy(ins,a,sizeof(ins));
        sort(ins+1,ins+n+1);
        len=unique(ins+1,ins+n+1)-ins-1;
        rt[0]=build(1,len);
        for(int i=1;i<=n;i++)
        {
            rt[i]=update(getid(a[i]),1,len,rt[i-1],1);
        }
        while(m--)
        {
            int l,r,k;
            read(l),read(r),read(k);
            printf("%lld
    ",ins[query(rt[l-1],rt[r],1,len,k)]);
        }
        return 0;
    }
    

    Warning:

    • ls[],rs[],sum[]等数组都要乘上 (2^5)
    • 离散化取lower_bound时,是最后减去0开头的地址,而不是1开头的地址。(即是lower_bound(ins+1,ins+n+1,x)-ins,而不是lower_bound(ins+1,ins+n+1,x)-ins-1
    • 查询时递归右子树时查找第 (k-|Son_{left}|) 小,而不是 (k) 小。
  • 相关阅读:
    recon-ng打开后显示No modules enabled/installed
    mysql的floor()报错注入方法详细分析
    视频合并时使用python批量修改文件名
    ThinkPHP5框架引入的css等外部资源文件没有生效
    用session实现的用户登陆,客户端是怎样获取到cookie信息的
    关于Linux系统下基于Tomcat部署和升级war包的详细过程
    nginx代理的页面性能优化大全
    linux下MySQL数据库的定时备份与定时删除
    关于linux查询内存,CPU,存储空间和日志查询的的常用命令及参数讲解
    linux 查看cpu核心数
  • 原文地址:https://www.cnblogs.com/nth-element/p/11785026.html
Copyright © 2020-2023  润新知