• poj 2104: K-th Number 【主席树】


    题目链接

    学习了一下主席树,感觉具体算法思路不大好讲。。

    大概是先建个空线段树,然后类似于递推,每一个都在前一个“历史版本”的基础上建立一个新的“历史版本”,每个历史版本只需占用树高个空间(好神奇!)

    查询时这道题是通过“历史版本”间作差解决

    *另外提一下,在建立“历史版本”的过程中,是“新建”,而不是“更新”,是先copy过来原有的,再按个人需求改成自己的,所产生的一个新的“历史版本”

    希望注释能有帮助

    //吐槽一下,本人看kuangbin模板看得好费劲,一方面不习惯用lson[],rson[]数组搞线段树,另一方面他的update query函数都是用二分写的,看半天没理解。。。

    以下是按个人习惯加了注释的代码

    #include<cstdio>  
    #include<queue>  
    #include<algorithm>  
    #include<cstdlib>  
    #include<cstring>  
    #include<iostream>  
    #include<cmath>  
    #define M 100000+5  
    using namespace std;  
    
    const int N=1e5+7;
    const int INF=0x3f3f3f3f;
    
    struct node
    {
        int l,r;    //l,r分别指向左右孩子,但由于历史的错乱,二者的值未必有规律 
        int size;    //当前历史版本里,该节点中所包含的元素个数
                    //p.s. 在最后一个历史版本里,size即为总元素个数n 
    }tr[N*20];
    //主席树的初始状态为一“空架子”
    //每次updata都在前一“历史版本”基础上新建h个结点,共updata n次,产生了n个历史版本,每个版本占用空间为h*sizeof(one node) 
    int tot;    //结点建立顺序|所赋标号 
    
    int n;        //共n个数 
    int a[N];    //读入数组
    int root[N];//记录a[]中每一个数对应各“历史版本”在主席树中的根结点标号 
    int m;        //t[]中元素个数 
    int t[N];    //离散数组
    int pos(int x)
    {
        return lower_bound(t+1,t+1+m,x)-t;
    }
    int q;        //q个询问
    
    int build(int l,int r)
    {
        int rt=tot++;
        tr[rt].size=0;
        if(l==r) return rt;
        int mid=(l+r)>>1;
        tr[rt].l=build(l,mid);
        tr[rt].r=build(mid+1,r);
        return rt;
    }
    
    //    pre_node:最近一历史,[l,r]当前所更新到的包含x的区间 
    //    x此次更新的“终点”
    //    沿途要增加的“权值”
    //    p.s.  1<=l<=r<=m 
    int updata(int pre_node,int l,int r,int x,int v)
    {
        int rt=tot++;            //rt为新结点的标号 
        tr[rt]=tr[pre_node];    //先拷贝原指向当前区间的结点
        tr[rt].size+=v;
        //    在函数不断向下深入过程中,更新node.size 
        if(l==r) return rt;
        int mid=(l+r)>>1;
        if(x<=mid)    tr[rt].l=updata(tr[pre_node].l,l,mid,x,v);
        else    tr[rt].r=updata(tr[pre_node].r,mid+1,r,x,v);
        return rt;
        //    在函数逐步return过程中,更新node.l|r 
    }
    
    //    ql,qr反应的是在a[]中的下标,对应着更新到该下标时的“历史版本” 
    //    l,r反应的是在t[]中的下标 
    int query(int ql,int qr,int l,int r,int k)
    {
        if(l==r) return l;
        int diff=tr[tr[qr].l].size-tr[tr[ql].l].size;//在更新到ql位置和更新到qr位置时两个“历史版本”下左孩子的size之差
                                                    // p.s. size的大小反应较小数的多少 
        int mid=(l+r)>>1;
        if(diff>=k) return query(tr[ql].l,tr[qr].l,l,mid,k);
        else return  query(tr[ql].r,tr[qr].r,mid+1,r,k-diff);
    }
    int main()
    {
        while(~scanf("%d%d",&n,&q))
        {
            for(int i=1;i<=n;i++)
                scanf("%d",&a[i]),t[i]=a[i];
            sort(t+1,t+1+n);
            m=unique(t+1,t+1+n)-t-1;
            m++,t[m]=INF;
            tot=0;
            root[0]=build(1,m);    //初始化一个空线段树
            for(int i=1;i<=n;i++)
            {
                int order=pos(a[i]);
                root[i]=updata(root[i-1],1,m,order,1);
                //每次在上一次已完成的基础上新增h个结点 
                //从根 逐步新建结点到 a[i]在线段树上所对应的位置 
                //在这个逐步新建结点的过程中,要新建的结点的l,r始终满足l<=m<=r
                //这些结点每一个的size都在pre_node的基础上+1 
            }
            while(q--)
            {
                int l,r,k;
                scanf("%d%d%d",&l,&r,&k);
                printf("%d
    ",t[query(root[l-1],root[r],1,m,k)]); 
             } 
        }
    }
  • 相关阅读:
    【BZOJ5281】Talent Show(分数规划)
    数据库的连接(学习笔记)
    锁(学习笔记)
    事务处理(学习笔记)
    游标(学习笔记)
    PL/SQL基础-异常处理
    通用函数(学习笔记)
    转换函数(学习笔记)
    数据库的备份和恢复(学习笔记学习中)
    分析函数
  • 原文地址:https://www.cnblogs.com/Just--Do--It/p/6523238.html
Copyright © 2020-2023  润新知