zoukankan      html  css  js  c++  java
  • 静态主席树学习小记

    语文不好的人的开头

    之前一直觉得主席树是一种黑科技,非常的神奇,区间第k大只会暴力或者分块

    其实也没有很复杂

    主席树是什么?

    你可以将它理解为对于i=1~n,对于每个1..i的前缀建了一棵线段树

    至于为什么是前缀,等下再说

    主席树解决什么问题?

    emmm目前蒟蒻我只会区间第k小

    主席树如何解决区间第k小问题呢?

    我们如果想要用线段树来写也很简单,只要对[l,r]这个区间建一棵线段树就行了,但这显然是O(Q*nlogn)的

    那么我们先引入前缀和求和的概念

    我们平时用前缀和,要求[l..r]的和时,都是这样:sum[r]-sum[l-1]

    然后我们再回想一下在线段树中我们是如何求第k小的

    很简单,我们对一个有序的序列建线段树,然后在一个节点处判断其左儿子的大小是否大于等于k,大于则k一定在左儿子内,否则k减去左儿子的大小,到右儿子中找

    那么主席树也是基本一样的,不过它要用到前缀和的思想

    首先我们知道,左儿子的大小就是小于等于mid的数的个数,那么1..r线段树的左儿子大小-1..l-1线段树的左儿子大小,不就是l..r线段树中左儿子的大小了吗?

    我这样说可能比较难懂,但是笔者条件所限,暂时无法作图

    这样时间复杂度解决了,可是空间复杂度并没有解决

    怎么解决空间复杂度呢?

    我们可以与之前建线段树共用大部分节点,真正修改的只有需要修改的一条链

    详情见代码

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N=2e5+10;
    struct Seg {
        int l,r,sz;
    }t[20*N];
    int cnt,root[N];//每个线段树的根
    struct ForSort {
        int a,id;
    }a[N];
    int rank[N];//离散化后的数组
    int n,m;
    
    bool CMP(ForSort a,ForSort b) {
        return a.a<b.a;
    }
    
    void Build_New(int &x,int l,int r,int val) {
        t[++cnt]=t[x];x=cnt;t[x].sz++;
        //在这里,我们显然不是要修改之前的树。
        //我们将之前的树中需要改的节点克隆一个出来,这样不用改的节点就被共用了。
        //我们只需要修改当前克隆出来的节点
        if (l==r) return;
        int mid=l+r>>1;
        if (val<=mid) Build_New(t[x].l,l,mid,val);
        else Build_New(t[x].r,mid+1,r,val);
    }
    
    int Query(int x,int y,int l,int r,int val) {
        int rk=t[t[y].l].sz-t[t[x].l].sz,mid=l+r>>1;
        if (l==r) return a[l].a;
        if (val<=rk) return Query(t[x].l,t[y].l,l,mid,val);
        else return Query(t[x].r,t[y].r,mid+1,r,val-rk);
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&a[i].a),a[i].id=i;
        sort(a+1,a+n+1,CMP);
        for (int i=1;i<=n;i++) rank[a[i].id]=i;
        for (int i=1;i<=n;i++) {
            root[i]=root[i-1];
            Build_New(root[i],1,n,rank[i]);
        }
        for (int i=1;i<=m;i++) {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d
    ",Query(root[l-1],root[r],1,n,k));
        }
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    自己动手写动态网站
    CompareValidator控件
    跨数据库服务器查询和跨表更新
    在Windows 2003 IIS 6.0中配置PHP的运行环境(图)
    sql语句跨服务器跨数据库执行
    ASP语法
    web 中 common
    common js
    经典的SQL面试题
    asp:TextBox 的ReadOnly属性 造成后台无法取到值
  • 原文地址:https://www.cnblogs.com/mastervan/p/10302667.html
Copyright © 2011-2022 走看看