马上就要省赛了,一方面是复习算法,另一个方面是为了总结模板
主席树是可持久化线段树,由n棵线段树组成,为了节约时间,利用了前缀和的思想,每一颗线段树维护的是前i 个数的信息,这样做差就能得到某个区间的信息。为了节约空间,查找到上一次的信息,不必每次都建立一颗完整的线段树,我们根据线段树的性质可以知道,每次只需要更新一条链的信息,从而大大减少空间。
建树部分和线段树类似
inline int build(int l,int r){ //先建1棵空树
int cnt=++k;
int mid=(l+r)/2;
if(l<r){
tree[cnt].l=build(l,mid);
tree[cnt].r=build(mid+1,r);
}
return cnt;
}
建树完成后,要用给定的数组进行建树,每次更新一条链即可,root数组存的是每个子线段树的根结点,这样就可以像查询一棵线段树一样查询主席树了。
inline int updata(int pre,int l,int r,int v){ //建n棵线段树
int cnt=++k;
tree[cnt].size=tree[pre].size+1;
tree[cnt].l=tree[pre].l; //继承上一个根结点的左右子树
tree[cnt].r=tree[pre].r;
int mid=(l+r)/2;
if(l<r){ //找到要更新的链
if(v<=mid) tree[cnt].l=updata(tree[pre].l,l,mid,v);
else tree[cnt].r=updata(tree[pre].r,mid+1,r,v);
}
return cnt;
}
inline int query(int fl,int fr,int l,int r,int k){ //查询操作
if(l==r){
return l;
}
int temp=tree[tree[fr].l].size-tree[tree[fl].l].size;
int mid=(l+r)/2;
if(k<=temp){
return query(tree[fl].l,tree[fr].l,l,mid,k);
}else return query(tree[fl].r,tree[fr].r,mid+1,r,k-temp);
}
一般部分题目给出的序列太大,要做离散化处理
即不改变原序列的相对大小关系,将其映射到1-n 来方便存储
void has(){
ans[1]=a[1].v;
b[a[1].i]=1;
for(int i=2;i<=n;i++){
if(a[i].v!=a[i-1].v) N++;
b[a[i].i]=N;
ans[N]=a[i].v;
}
}
下面给出洛谷的主席树模板题的AC代码
/*
主席树模板
*/
#include <bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;
struct node {
int l,r;
int size;
}tree[MAX<<5];
struct nodee{
int v,i;
}a[MAX];
int root[MAX<<5],k,N=1,ans[MAX],b[MAX],n;
bool cmp(const struct nodee a ,const struct nodee b){
return a.v<b.v;
}
void has(){
ans[1]=a[1].v;
b[a[1].i]=1;
for(int i=2;i<=n;i++){
if(a[i].v!=a[i-1].v) N++;
b[a[i].i]=N;
ans[N]=a[i].v;
}
}
inline int build(int l,int r){ //先建n棵空树
int cnt=++k;
int mid=(l+r)/2;
if(l<r){
tree[cnt].l=build(l,mid);
tree[cnt].r=build(mid+1,r);
}
return cnt;
}
inline int updata(int pre,int l,int r,int v){
int cnt=++k;
tree[cnt].size=tree[pre].size+1;
tree[cnt].l=tree[pre].l;
tree[cnt].r=tree[pre].r;
int mid=(l+r)/2;
if(l<r){
if(v<=mid) tree[cnt].l=updata(tree[pre].l,l,mid,v);
else tree[cnt].r=updata(tree[pre].r,mid+1,r,v);
}
return cnt;
}
inline int query(int fl,int fr,int l,int r,int k){
if(l==r){
return l;
}
int temp=tree[tree[fr].l].size-tree[tree[fl].l].size;
int mid=(l+r)/2;
if(k<=temp){
return query(tree[fl].l,tree[fr].l,l,mid,k);
}else return query(tree[fl].r,tree[fr].r,mid+1,r,k-temp);
}
int main(){
int m;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&a[i].v);
a[i].i=i;
}
sort(a+1,a+n+1,cmp);
has();
root[0]=build(1,n);
for(int i=1;i<=n;i++){
root[i]=updata(root[i-1],1,n,b[i]);
}
int l,r,k;
for(int i=0;i<m;i++){
cin>>l>>r>>k;
cout<<ans[query(root[l-1],root[r],1,n,k)]<<endl;
}
return 0;
}