主席树(可持久化权值线段树)入门学习笔记

前置知识:可持久化线段树权值线段树

参考大佬博客:「黑历史」浅谈权值线段树到主席树   (想直接看主席树的话就跳到中间部分开始看)

 总述:

  (以求区间第k小为例)值域线段树维护一个单调递增的值域的数的出现次数。主席树将原序列的下标看做时间戳,这里还应用了前缀和、前缀可减性的思想。把求原序列区间[l,r]上的答案(这里为第k小的数)的问题,转化为将第r个版本与第l-1个版本的可持久化值域线段树相减后得到新树,再从新树上求全局答案的问题。但实际操作中并不用真的做一次线段树合并(相减),只需在这两棵线段树上从根开始同时走,记第r个版本与第l-1个版本的线段树当前节点的键值(这里为当前节点代表值域中的数的出现次数)分别为v、u,只需将k与u-v相比后 得出答案/递归就好(若向右递归,别忘k-=u-v)。

模板题:【模板】可持久化线段树 2(主席树)

  题解口胡:

    先离散化。然后按总述说的做。

ac代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 
  5 #define qiumid (l+r>>1)
  6 
  7 using namespace std;
  8 
  9 const int N=2e5+5;
 10 
 11 int n,tre[N*23],cnt,hed[N],lson[N*23],rson[N*23],m,nn;
 12 int dic[N];
 13 
 14 struct node{
 15     int num,ord,def;
 16 }ai[N];
 17 
 18 inline int read()//N
 19 {
 20     int x=0;
 21     bool f=0;
 22     char ch=getchar();
 23     while(!isdigit(ch))
 24         f|=ch=='-',ch=getchar();
 25     while(isdigit(ch))
 26         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
 27     return f?-x:x;
 28 }
 29 
 30 inline bool cmp1(const node &a,const node &b)
 31 {
 32     return a.num<b.num;
 33 }
 34 
 35 inline bool cmp2(const node &a,const node &b)
 36 {
 37     return a.ord<b.ord;
 38 }
 39 
 40 void build(int t,int l,int r)
 41 {
 42     if(l==r)
 43         return;
 44     int mid=qiumid;
 45     lson[t]=++cnt;
 46     build(cnt,l,mid);
 47     rson[t]=++cnt;
 48     build(cnt,mid+1,r);
 49 }
 50 
 51 void insert(int u,int t,int l,int r,int w)
 52 {
 53     if(l==r)
 54     {
 55         tre[u]=tre[t]+1;
 56         return;
 57     }
 58     int mid=qiumid;
 59     if(w<=mid)
 60     {
 61         rson[u]=rson[t];
 62         lson[u]=++cnt;
 63         insert(cnt,lson[t],l,mid,w);
 64     }
 65     else
 66     {
 67         lson[u]=lson[t];
 68         rson[u]=++cnt;
 69         insert(cnt,rson[t],mid+1,r,w);
 70     }
 71     tre[u]=tre[lson[u]]+tre[rson[u]];
 72 }
 73 
 74 int fin(int u,int v,int l,int r,int k)
 75 {
 76     if(l==r)
 77         return l;
 78     int kk;
 79     if((kk=tre[lson[v]]-tre[lson[u]])>=k)
 80         return fin(lson[u],lson[v],l,qiumid,k);
 81     else
 82         return fin(rson[u],rson[v],qiumid+1,r,k-kk);
 83 }
 84 
 85 int main()
 86 {
 87 //  freopen(".in","r",stdin);
 88 //  freopen(".out","w",stdout);
 89     n=read();m=read();
 90     for(int i=1;i<=n;++i)
 91     {
 92         ai[i].num=read();
 93         ai[i].ord=i;
 94     }
 95     sort(ai+1,ai+1+n,cmp1);
 96     ai[1].def=nn=1;
 97     dic[1]=ai[1].num;
 98     for(int i=2;i<=n;++i)
 99     {
100         if(ai[i].num==ai[i-1].num)
101             ai[i].def=nn;
102         else
103         {
104             ai[i].def=++nn;
105             dic[nn]=ai[i].num;
106         }
107     }
108     sort(ai+1,ai+1+n,cmp2);
109     hed[0]=cnt=1;
110     build(1,1,nn);
111     for(int i=1;i<=n;++i)
112     {
113         hed[i]=++cnt;
114         insert(cnt,hed[i-1],1,nn,ai[i].def);
115     }
116     int ll,rr,k;
117     for(int i=1;i<=m;++i)
118     {
119         ll=read(),rr=read(),k=read();
120         printf("%d\n",dic[fin(hed[ll-1],hed[rr],1,nn,k)]);
121     }
122     return 0;
123 }

应用:

  静态区间(即无修改,有修改请去隔壁树套树)第k大

   

 

posted @ 2020-11-25 11:35  千叶繁华  阅读(158)  评论(0编辑  收藏  举报