zoukankan      html  css  js  c++  java
  • 主席树 学习笔记

    对于询问$[1,n]$的第$k$小数,我们都知道直接上权值线段树就行了。那么对于任意区间的第$k$小数呢?

    暴力一点,每次开一颗线段树。空间肯定爆炸。那么此时,主席树便应运而生。

    主席树的主要思想就是:保留每次插入操作时的历史版本,以便查询区间第$k$小的数。先说流程。

    1.先建一颗空的权值线段树,$[1,len]$。

    2.从$1$到$n$对于每个结点都新建一颗权值线段树,$i$结点的线段树根据$i-1$的更新,即在原基础上进行加的操作。

    3.若查询$[l,r]$则拿出第$l-1$颗和第$r$颗线段树进行比较,他们之间的差值就是$[l,r]$区间的元素个数。查找第$k$小,就看左儿子的大小$x$。如果$kleq x$,那么答案肯定在左儿子,反之则在右儿子。注意此时$k$要更新成$k-x$,因为在右儿子的区间里相对大小会发生变化。

    主席树有着满足前缀和和树上差分等优秀性质(感性理解),所以不管是树还是序列都可以维护。

    注意:使用主席树时请不要吝啬你的空间,不然会出现奇奇怪怪的错误。一般来说我都开$nlog n$,实际上开$8*10^6$都可以。

    更多内容详见OI Wiki。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int maxn=5000005;
    int n,m,len,a[maxn],b[maxn],sum[maxn],ls[maxn],rs[maxn],tot,rt[maxn];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline int getpos(int x){return lower_bound(b+1,b+len+1,x)-b;}
    inline int build(int l,int r)
    {
        int root=++tot;
        if (l==r) return root;
        int mid=(l+r)>>1;
        ls[root]=build(l,mid);
        rs[root]=build(mid+1,r);
        return root;
    }
    inline int update(int k,int l,int r,int root)
    {
        int dir=++tot;
        ls[dir]=ls[root],rs[dir]=rs[root];sum[dir]=sum[root]+1;
        int mid=(l+r)>>1;
        if (l<r)
        {
            if (k<=mid) ls[dir]=update(k,l,mid,ls[root]);
            else rs[dir]=update(k,mid+1,r,rs[root]);
        }
        return dir;
    }
    inline int query(int u,int v,int l,int r,int k)
    {
        if (l==r) return l;
        int x=sum[ls[v]]-sum[ls[u]],mid=(l+r)>>1;
        if (k<=x) return query(ls[u],ls[v],l,mid,k);
        else return query(rs[u],rs[v],mid+1,r,k-x);
    }
    signed main()
    {
        n=read(),m=read();
        for (int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
        sort(b+1,b+n+1);
        len=unique(b+1,b+n+1)-b-1;
        rt[0]=build(1,len);
        for (int i=1;i<=n;i++) rt[i]=update(getpos(a[i]),1,len,rt[i-1]);
        while(m--)
        {
            int l=read(),r=read(),k=read();
            printf("%lld
    ",b[query(rt[l-1],rt[r],1,len,k)]);
        }
        return 0;
    }
  • 相关阅读:
    有用的网站
    RMVANNUAL matlab remove annual cycle of a time series
    [转载]grdcontour命令在GMT4下绘制等值线图
    Filter应用之-自动登录
    Filter应用之-验证用户是否已经登录
    Filter应用之2-设置某些页面缓存或是不缓存
    过虑器应用之1-设置request编码
    过滤器Filter
    java文件下载
    用COS实现文件上传
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13332357.html
Copyright © 2011-2022 走看看