zoukankan      html  css  js  c++  java
  • 「可持久化线段树」学习笔记

    • 前言

      未经允许,请勿转载。

      前置知识:线段树。

      主席树是一个东西的数据结构。

      竟然一遍AC。太难得了。

      主要参考这个这个这个


    • 求什么

      比如洛谷的P3834 【模板】可持久化线段树 1(主席树)

      给定 (n) 个整数构成的序列,将对于指定的闭区间查询其区间内的第 (k) 小值。

      其中,对于 (100\%) 的数据满足:(1 leq n,m leq 2 imes 10^5 , -10^9 le a_i le 10^9)


    • 怎么求 & 主席树的思想

      比如一个序列 (25957,6405,15770,26287,26465)

      因为只是要求第 (k) 小值,所以我们可以考虑先把原序列 (a_i) 离散化

      所以离散后成了这样 (3,1,2,4,5)

      然后我们要先建一个空的线段树。

      然后对于每个区间 ([1,i]) 建一个线段树

      对于每个线段树,存上 ([1,i]) 中所有值离散后的数。

      如图,为各个区间的线段树:

      那么接下来解决的就是查询,求区间 ([l,r]) 中第 (k) 小的值。

      比如,要查询的是 ([2,4]) 中第 (2) 大的数。

      我们选择 ([1,1])([1,4]) 这两个线段树:

      然后把他们对应点相减:

      我们设每个点(区间)的值为(sum[l,r]),然后递归查询。

      第一个节点的两个儿子 ([1,2])([3,5]) ,要查找的是第 (k=2) 个,因为 (sum[1,2] ge k) ,所以选择 ([1,2])

      ([1,1])([2,2]) 中选择,因为 (sum[1,1] < k) ,所以选择 ([2,2]) 并且把查找第 (k)改为查找 (k-sum[1,1])

      因为此时 (l=r) ,查找结束,结果为 (2)

      然后就解决了。

      然后发现空间时间炸了。

      在上述过程中,其实可以发现,相邻的线段树,即 ([1,i])([1,i+1]) 这两个区间只有一条链不同。

      所以我们可以直接把树建成这样:

      这样时间和空间的问题也解决了。

      那么也就解决了这个问题。


    • 代码

      #include<iostream>
      #include<cstdio>
      #include<algorithm>
      using namespace std;
      const int Maxn=2e5+5,Maxm=(Maxn<<5)+5;
      int n,m,p,cntn,a[Maxn],b[Maxn];
      int lc[Maxm],rc[Maxm],rt[Maxm],sum[Maxm];
      void build(int &o,int l,int r)//建个空线段树 
      {	
          o=++cntn;
          if(l==r)return;
          int mid=(l+r)>>1;
          build(lc[o],l,mid);
          build(rc[o],mid+1,r);
      }
      int modify(int o,int l,int r)
      {	
          int oo=++cntn;
          lc[oo]=lc[o];rc[oo]=rc[o];
          sum[oo]=sum[o]+1;
          if(l==r)return oo;
          int mid=(l+r)>>1;
          if(p<=mid)lc[oo]=modify(lc[oo],l,mid);
          else rc[oo]=modify(rc[oo],mid+1,r);
          return oo;
      }
      int query(int u,int v,int l,int r,int k) //查询 
      {	
          int mid=(l+r)>>1,x=sum[lc[v]]-sum[lc[u]];
          if(l==r)return l;
          if(x>=k)return query(lc[u],lc[v],l,mid,k);
          else return query(rc[u],rc[v],mid+1,r,k-x);
      }
      int main()
      {	
          scanf("%d%d",&n,&m);
          for(int i=1;i<=n;i++)
          {	
              scanf("%d",&a[i]);
              b[i]=a[i];
          }
          sort(b+1,b+1+n); //离散 
          int q=unique(b+1,b+1+n)-b-1; //去重 
          build(rt[0],1,q);
          for(int i=1;i<=n;i++)
          {	
              p=lower_bound(b+1,b+1+q,a[i])-b;
              rt[i]=modify(rt[i-1],1,q);
          }
          while(m--)
          {	
              int x,y,z;
              scanf("%d%d%d",&x,&y,&z);
              printf("%d
      ",b[query(rt[x-1],rt[y],1,q,z)]);
          }
          return 0;
      }
      

    [ ext{by Rainy7} ]

  • 相关阅读:
    制衡技术的新蓝海
    制衡技术,从Adblock所想到的
    centos6中安装新版 Elasticsearch 7.x
    nrm 安装与npm镜像切换
    james2.3 配置收件 之 MariaDB数据库配置
    手动搭建apache james邮件服务器,实现邮件功能
    James 如何作为服务在后台启动
    安装最新版RabbitMQ v3.7.13 以及基本配置
    mac 下 通过 brew 安装 MariaDB
    Mac 上安装maven
  • 原文地址:https://www.cnblogs.com/Rainy7/p/12378512.html
Copyright © 2011-2022 走看看