此模板作用为经典的查询区间第k大
不带修改:复杂度O(nlogn)
带修改:复杂度O(n∗(logn)^2)
不带修改的主席树就是在前一棵树的基础上重建其中一条链,其余的链都和原来的公用,相当于logn的复杂度新建一颗线段树
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=8e5+5;
int T[maxn],ls[maxm],rs[maxm],sum[maxm];
int a[maxn],b[maxn];
int n,m,cnt;
int build(int l,int r)
{
int now=++cnt;
if(l<r)
{
int mid=l+r>>1;
ls[now]=build(l,mid);
rs[now]=build(mid+1,r);
}
return now;
}
void build_new(int id,int pos)
{
T[id]=++cnt;
sum[cnt]=sum[T[id-1]]+1;
int l=1,r=m,now=cnt,pre=T[id-1];
while(l<r)
{
int mid=l+r>>1;
if(pos>mid)
{
ls[now]=ls[pre];
rs[now]=++cnt;
sum[rs[now]]=sum[rs[pre]]+1;
now=rs[now];
pre=rs[pre];
l=mid+1;
}
else
{
rs[now]=rs[pre];
ls[now]=++cnt;
sum[ls[now]]=sum[ls[pre]]+1;
now=ls[now];
pre=ls[pre];
r=mid;
}
}
}
int query(int a,int b,int l,int r,int k)
{
if(l==r) return l;
int all=sum[ls[b]]-sum[ls[a]];
int mid=l+r>>1;
if(k<=all)
return query(ls[a],ls[b],l,mid,k);
return query(rs[a],rs[b],mid+1,r,k-all);
}
int main()
{
int q;
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
T[0]=build(1,m);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+m+1,a[i])-b;
build_new(i,pos);
}
for(int i=1;i<=q;i++)
{
int x,y,k;
cin>>x>>y>>k;
cout<<b[query(T[x-1],T[y],1,m,k)]<<endl;
}
return 0;
}
待修改的主席树需要利用树状数组维护更新信息
为什么要用树状数组来维护呢,因为用树状数组可以在logn的复杂度下重组出任意一点的修改信息
乍听起来比较复杂,实际上原理非常简单,只是实现较为麻烦
当出现更新操作时,显然我们不能把所有的树都重新构建一遍,那么对于更新, 我们不修改这些已经建好的树, 而去新建一批树S,用来记录更新,而这批线段树,我们用树状数组来维护,仅需要logn的复杂度即可完成查询
划重点:此树状数组的每个节点都是一颗线段树
对于每个点的询问,其真实值为,即原来的值加上S树层层叠加得到的值
具体来说,对于k位置的询问,其真实值就是原来树k位置的值加上所有相关S树的k位置的值的总和
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=8e5+5;
int n,m,cnt;
struct que
{
char s[3];
int l,r,k;
}Q[maxn];
int T[maxn],S[maxn],ls[maxm],rs[maxm],sum[maxm];
int a[maxn],b[maxn];
int use[maxn][2];
int lowbit(int x)
{
return x&-x;
}
int build(int l,int r)
{
int now=++cnt;
if(l<r)
{
int mid=l+r>>1;
ls[now]=build(l,mid);
rs[now]=build(mid+1,r);
}
return now;
}
int build_new(int id,int pos,int op,int v)
{
int root=++cnt;
int pre;
if(id==1)
pre=T[0];
else pre=op?S[id-1]:T[id-1];
if(op&&v) pre=S[id];
sum[root]=sum[pre]+v;
int l=1,r=m,now=root;
while(ls[pre]||rs[pre])
{
int mid=l+r>>1;
if(pos>mid)
{
ls[now]=ls[pre];
rs[now]=++cnt;
sum[rs[now]]=sum[rs[pre]]+v;
pre=rs[pre];
now=rs[now];
l=mid+1;
}
else
{
rs[now]=rs[pre];
ls[now]=++cnt;
sum[ls[now]]=sum[ls[pre]]+v;
pre=ls[pre];
now=ls[now];
r=mid;
}
}
return root;
}
void update(int loc,int newval)
{
int pos=lower_bound(b+1,b+m+1,a[loc])-b;
int x=loc;
while(x<=n)
{
S[x]=build_new(x,pos,1,-1);
x+=lowbit(x);
}
pos=lower_bound(b+1,b+m+1,newval)-b;
x=loc;
while(x<=n)
{
S[x]=build_new(x,pos,1,1);
x+=lowbit(x);
}
a[loc]=newval;
return ;
}
void work(int l,int r,int op)
{
if(op==0)
{
while(l)
{
use[l][0]=ls[use[l][0]];
l-=lowbit(l);
}
while(r)
{
use[r][1]=ls[use[r][1]];
r-=lowbit(r);
}
}
else
{
while(l)
{
use[l][0]=rs[use[l][0]];
l-=lowbit(l);
}
while(r)
{
use[r][1]=rs[use[r][1]];
r-=lowbit(r);
}
}
}
int getsum(int pos,int op)
{
int res=0;
while(pos)
{
res+=sum[ls[use[pos][op]]];
pos-=lowbit(pos);
}
return res;
}
int query(int L,int R,int a,int b,int l,int r,int k)
{
if(l==r) return l;
int all=getsum(R,1)-getsum(L,0)+sum[ls[b]]-sum[ls[a]];
int mid=l+r>>1;
if(k<=all)
{
work(L,R,0);
return query(L,R,ls[a],ls[b],l,mid,k);
}
else
{
work(L,R,1);
return query(L,R,rs[a],rs[b],mid+1,r,k-all);
}
}
int main()
{
int q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i];
int all=n;
for(int i=1;i<=q;i++)
{
scanf("%s%d%d",Q[i].s,&Q[i].l,&Q[i].r);
if(Q[i].s[0]=='Q')
scanf("%d",&Q[i].k);
else b[++all]=Q[i].r;
}
sort(b+1,b+all+1);
m=unique(b+1,b+all+1)-b-1;
T[0]=build(1,m);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+m+1,a[i])-b;
T[i]=build_new(i,pos,0,1);
}
for(int i=1;i<=n;i++)
S[i]=build_new(i,1,1,0);
for(int i=1;i<=q;i++)
{
if(Q[i].s[0]=='Q')
{
int x=Q[i].l-1,y=Q[i].r;
while(x)
{
use[x][0]=S[x];
x-=lowbit(x);
}
while(y)
{
use[y][1]=S[y];
y-=lowbit(y);
}
printf("%d\n",b[query(Q[i].l-1,Q[i].r,T[Q[i].l-1],T[Q[i].r],1,m,Q[i].k)]);
}
else update(Q[i].l,Q[i].r);
}
return 0;
}
上述代码支持两种操作,Q操作表示查询,C操作表示更新
更新于2019/8/1:
全新的主席树模板,省去了初始的建树操作
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=4e6+5;
int T[maxn],ls[maxm],rs[maxm],sum[maxm];
int a[maxn],b[maxn];
int n,m,cnt;
void insert(int &x,int pre,int l,int r,int pos)
{
if(!x) x=++cnt;
sum[x]=sum[pre]+1;
if(l==r) return ;
int mid=(l+r)/2;
if(pos<=mid) insert(ls[x],ls[pre],l,mid,pos),rs[x]=rs[pre];
else insert(rs[x],rs[pre],mid+1,r,pos),ls[x]=ls[pre];
}
int query_k(int a,int b,int l,int r,int k)
{
if(l==r) return l;
int all=sum[ls[b]]-sum[ls[a]];
int mid=(l+r)/2;
if(k<=all) return query_k(ls[a],ls[b],l,mid,k);
return query_k(rs[a],rs[b],mid+1,r,k-all);
}
int query_num(int a,int b,int L,int R,int l,int r)
{
if(L<=l&&r<=R) return sum[b]-sum[a];
int mid=(l+r)/2;
int res=0;
if(L<=mid) res+=query_num(ls[a],ls[b],L,R,l,mid);
if(R>mid) res+=query_num(rs[a],rs[b],L,R,mid+1,r);
return res;
}
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+m+1,a[i])-b;
insert(T[i],T[i-1],1,m,pos);
}
for(int i=1;i<=q;i++)
{
int x,y,op,k,l,r;
cin>>x>>y>>op;
if(op==1)
{
cin>>k;
cout<<b[query_k(T[x-1],T[y],1,m,k)]<<endl;
}
else
{
cin>>l>>r;
cout<<query_num(T[x-1],T[y],l,r,1,m)<<endl;
}
}
return 0;
}