此模板作用爲經典的查詢區間第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(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]]+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操作表示更新