主席树(模板)
前置知识:线段树
题意
给出一个数组,每次询问区间第 k k k小。
思路
主席树是一种线段树,其对于每一个前缀维护值域。
对于原数组 S : S: S: [ 4 , 7 , 5 , 3 , 6 , 1 , 1 , 3 ] [4 ,7 ,5 ,3 ,6,1 ,1 ,3] [4,7,5,3,6,1,1,3]
我们先将其排序去重
可得到 T T T: [ 1 , 3 , 4 , 5 , 6 , 7 ] [1,3,4,5,6,7] [1,3,4,5,6,7]
对新的数组的前缀 [ 1 , 8 ] [1,8] [1,8]进行维护 可以得到这样一棵线段树
其中黑色的数字代表维护的区间范围,红色的数字代表节点的权值。
[ a , b ] [a,b] [a,b]有权值 c c c其代表的含义是 数组 S S S中,大于 T [ a ] T[a] T[a]且小于 T [ b ] T[b] T[b]的数有 c c c个。
我们如果要求 [ 1 , 8 ] [1,8] [1,8]之间的第2大
在第一个节点,他的左儿子权值是5,右儿子是3,所以第二大在左儿子中,重复这个过程,可以求得第2大。区间 [ 3 , 8 ] [3,8] [3,8]同理,利用前缀和的思想,我们可以用 [ 1 , 2 ] , [ 1 , 8 ] [1,2],[1,8] [1,2],[1,8]的两棵线段树求出第 k k k大。
空间问题?
如果我们对每一个前缀开一棵线段树,它显然会 M L E MLE MLE,我们发现,不同线段树中会有很大的重复部分,于是我们可以每次只建不同的部分来节省空间。
接上面的例子,对于数组 S : S: S: [ 4 , 7 , 5 , 3 , 6 , 1 , 1 , 3 ] [4 ,7 ,5 ,3 ,6,1 ,1 ,3] [4,7,5,3,6,1,1,3]
黑色的是前缀 [ 1 , 1 ] [1,1] [1,1]的线段树,绿色的则是前缀 [ 1 , 2 ] [1,2] [1,2]的线段树。我们发现每次新建一棵线段树,只要增加 l o g n logn logn个点(一条链) 所以总空间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的。
###代码
/*
,]@@@@@@@@@@@\] *@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@*
/@@@@@@@@@@@@@@@@@@@\` *@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@*
=/@@@@@@@/[ ,[\@@@@@@@` *[[[[[[[[[[@@@@@/[[[[[[[[[[ @@@@@*
=@@@@@@` *\@@@@@\ @@@@@^ @@@@@*
/@@@@@^ *@@@@@@* @@@@@^ @@@@@*
=@@@@@` \@@@@@ @@@@@^ @@@@@*
*@@@@@^ *@@@@@^ @@@@@^ @@@@@*
=@@@@@ =@@@@@ @@@@@^ @@@@@*
@@@@@^ =@@@@@ @@@@@^ @@@@@*
@@@@@^ =@@@@@ @@@@@^ @@@@@*
@@@@@^ =@@@@@ @@@@@^ @@@@@*
=@@@@@ =@@@@/ @@@@@^ @@@@@*
,@@@@@^ *@@@@@` @@@@@^ @@@@@*
=@@@@@` @@@@@/ @@@@@^ @@@@@*
\@@@@@^ ,@@@@@/ @@@@@^ @@@@@*
=@@@@@@] ,@@@@@@^ @@@@@^ @@@@@*
\@@@@@@@@]]]]]]]/@@@@@@@/ @@@@@^ @@@@@@@@@@@@@@@@@@@`
[@@@@@@@@@@@@@@@@@@@` @@@@@^ @@@@@@@@@@@@@@@@@@@^
[\@@@@@@@@@[[ [[[[[` [[[[[[[[[[[[[[[[[[[`
,@@@@@\ ,@@@@@` =@@@@@ =@@@@@@@@@@@@@@@@@@@@@@@@@@
,@@@@@\ ,@@@@@` =@@@@@ =@@@@@@@@@@@@@@@@@@@@@@@@/
,@@@@@\ ,@@@@@` =@@@@@ ,@@@@@`
*@@@@@^ ,@@@@@` =@@@@@ /@@@@@
*@@@@@^ ,@@@@@` =@@@@@ *@@@@@^
*@@@@@^ ,@@@@@* =@@@@@ ,@@@@@`
*@@@@@^ ,@@@@@* =@@@@@ /@@@@/
*@@@@@^ ,@@@@@ =@@@@@ ,@@@@@^
*@@@@@\@@@@@ =@@@@@ =@@@@@`
*@@@@@@@@@ =@@@@@ /@@@@/
*@@@@@@@ =@@@@@ ,@@@@@`
*@@@@@ =@@@@@ =@@@@@*
*@@@@@ =@@@@@ *@@@@@/
*@@@@@ =@@@@^ ,@@@@@`
*@@@@@ ,@@@@@* /@@@@@
*@@@@@ ,@@@@@` *@@@@@/
*@@@@@ =@@@@@@@@@@@` ,@@@@@@@@@@@@@@@@@@@@@@@@@@^
*@@@@@ =@@@@@@@@/` =@@@@@@@@@@@@@@@@@@@@@@@@@@@^
*/
#include<bits/stdc++.h>
#define ll long long
#define N 200015
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
using namespace std;
int n,m,q,cnt,a[N],b[N],rt[N<<5],ls[N<<5],rs[N<<5],sum[N<<5];
int build(int l,int r){
int root = ++cnt;
int mid = (l+r)>>1;
if(l < r){
ls[root] = build(l,mid);
rs[root] = build(mid+1,r);
}
return root;
}
inline int update(int pre,int l,int r,int x){
int root = ++cnt;
ls[root] = ls[pre]; rs[root] = rs[pre];sum[root] = sum[pre]+1;
int mid = (l+r)>>1;
if(l < r){
if(x <= mid) ls[root] = update(ls[pre],l,mid,x);
else rs[root] = update(rs[pre],mid+1,r,x);
}
return root;
}
int query(int u,int v,int l,int r,int k){
if(l >= r) return l;
int mid = (l+r)>>1;
int x = sum[ls[v]] - sum[ls[u]];
if(x >= k) return query(ls[u],ls[v],l,mid,k);
else return query(rs[u],rs[v],mid+1,r,k-x);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for(int i = 1;i <= n;++i) cin >> a[i],b[i] = a[i];
sort(b+1,b+n+1);
int q = unique(b+1,b+n+1)-b-1;
rt[0] = build(1,q);
for(int i = 1;i <= n;++i){
int t = lower_bound(b+1,b+q+1,a[i])-b;
rt[i] = update(rt[i-1],1,q,t);
}
for(int i = 1;i <= m;++i){
int l,r,k;
cin>>l>>r>>k;
cout << b[query(rt[l-1],rt[r],1,q,k)] << endl;
}
return 0;
}
正好一百行。