主席树
新学了个东西---主席树。。。又名可持久化线段树
主席树最基础的应用就是 第K极值。(前提是静态,动态有动态的主席树)
首先第一大操作:建基树
主席树是 1~n 每个节点都建一颗树,节点 rt 表示的是 1~rt 的什么什么值
因为每个节点建的是值域线段树,所以要离散化(不然空间会爆)
离散好后,先建个虚拟节点 0的以这个为基准的树(我这里简称基树)。
建树过程和普通线段树差不多,分左右区间,只不过父节点与子节点的关系不再是 k<<1 和 k<<1|1,而是 dfs序。
接着是第二大操作:建其他树
1~n, 依次按原顺序加入节点。可因为每次操作最多只会涉及 logn 个节点,如果每个节点都建一颗线段树的话,空间可能承受不了。所以我们采用与前面树共用节点的方法。
对于那些本次操作不涉及的节点,还是用原标号(具体操作是把根节点的边直接连向原节点),对于那些涉及的点,则只能另开新节点(注意涉及到的节点对应节点的什么什么值相比前面的要更新!!!)(具体操作见代码)
第三大操作:查询第 k 极值。
这里分两种:
1、1~i 的极值
直接查询就可以了,和平衡树、二叉排序树一样的查法,分左右两个比大小。
2、l~r 的极值
就要用到前缀和的思想。设 x= sum[r]-sum[l],则如果 k<=x 则在树的左儿子,反之则在右儿子。(其实和上面差不多,只不过要用前缀和作差)(比较抽象,可能比较难理解,自己到网上找找博客,画画图,理解理解)。
主席树的最基本操作基本就是这些(待填坑。。。)
落谷模板题:AAAAAAAAAAAAAAAA
贴代码:
1 #include<bits/stdc++.h> 2 #define mid (l+r)/2 3 using namespace std; 4 const int N=2e5+5; 5 int n,q,m,cnt=0; 6 int a[N],b[N],T[N]; 7 int sum[N<<5],L[N<<5],R[N<<5]; 8 9 inline int build(int l,int r) 10 { 11 int rt=++cnt; 12 sum[rt]=0; 13 if (l<r) 14 { 15 L[rt]=build(l,mid); 16 R[rt]=build(mid+1,r); 17 } 18 return rt; 19 } 20 inline int update(int lst,int l,int r,int w) 21 { 22 int rt=++cnt; 23 L[rt]=L[lst],R[rt]=R[lst]; sum[rt]=sum[lst]+1; 24 if (l<r) 25 { 26 if (w<=mid) L[rt]=update(L[lst],l,mid,w); 27 else R[rt]=update(R[lst],mid+1,r,w); 28 } 29 return rt; 30 } 31 inline int query(int u,int v,int l,int r,int k) 32 { 33 if (l>=r) return l; 34 int x=sum[L[v]]-sum[L[u]]; 35 if (x>=k) return query(L[u],L[v],l,mid,k); 36 else return query(R[u],R[v],mid+1,r,k-x); 37 } 38 int main() 39 { 40 scanf("%d%d",&n,&q); 41 for (int i=1; i<=n; ++i) 42 { 43 scanf("%d",&a[i]); 44 b[i]=a[i]; 45 } 46 sort(b+1,b+1+n); 47 m=unique(b+1,b+1+n)-b-1; 48 T[0]=build(1,m); 49 for (int i=1; i<=n; ++i) 50 { 51 int t=lower_bound(b+1,b+1+m,a[i])-b; 52 T[i]=update(T[i-1],1,m,t); 53 } 54 while (q--) 55 { 56 int x,y,z; 57 scanf("%d%d%d",&x,&y,&z); 58 int t=query(T[x-1],T[y],1,m,z); 59 printf("%d ",b[t]); 60 } 61 return 0; 62 }
fighting fighting fighting !!!