关于主席树的介绍有一篇博客很详细,适合入门:主席树入门
主席树需要的前置知识有:前缀和和线段树
1.主席树上的差分区间与前缀和有关,而主席树的建树,修改,查询和线段树很相似.学了线段树之后看主席树就显得没有那么困难。
2.由于主席树对于一般的线段树多了一个logn的空间的大小,因此需要注意空间防止MLE。主席树的用法非常灵活,主席树最重要的是他的算法是在线的。
3.主席树是一个可持久化的线段树,可以对于历史的版本记录,可以查询之前版本的记录。
主席树建树时间复杂度:O(nlogn)
查询时间复杂度:O(mlogn)
总时间复杂度:O((n+m)logn)
空间复杂度:O(nlog^2 n)
1.建树
lc,rc表示左右子节点
sum 表示当前节点权值
void build(int &rt,int l,int r)
{
rt=++sz,sum[rt]=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(lc[rt],l,mid),build(rc[rt],mid+1,r);
}
2.修改
每次update函数应首先将之前的左右子节点赋予给当前节点,然后在根据需要修改的位置重新创建左右子节点。在赋予左右节点时以将历史数据全部转移到当前的版本中。
int update(int o,int l,int r)
{
int oo=++sz;
lc[oo]=lc[o],rc[oo]=rc[o],sum[oo]=sum[o]+1; //权值+1
if(l==r) return oo;
int mid=(l+r)>>1;
if(p<=mid) lc[oo]=update(lc[oo],l,mid); //只对改动的点进行建点 p为本次修改的权值
else rc[oo]=update(rc[oo],mid+1,r);
return oo;
}
3.查询
int query(int u,int v,int l,int r,int k)
{
int mid=(l+r)>>1;
int x=sum[lc[v]]-sum[lc[u]];
if(l==r) return l;
if(x>=k) return query(lc[u],lc[v],l,mid,k);
else return query(rc[u],rc[v],mid+1,r,k-x);
}
例题
主席树板子
思路:把query函数改一下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=5e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,p,q,sz=0,k,ans=0,pos;
int a[N],b[N];
int rt[N<<5],lc[N<<5],rc[N<<5],sum[N<<5];
char s[5];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void build(int &rt,int l,int r)
{
rt=++sz,sum[rt]=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(lc[rt],l,mid),build(rc[rt],mid+1,r);
}
int update(int o,int l,int r)
{
int oo=++sz;
lc[oo]=lc[o],rc[oo]=rc[o],sum[oo]=sum[o]+1;
if(l==r)
return oo;
int mid=(l+r)>>1;
if(p<=mid) lc[oo]=update(lc[oo],l,mid);
else rc[oo]=update(rc[oo],mid+1,r);
return oo;
}
void query(int u,int v,int l,int r,int mm)
{
int mid=(l+r)>>1;
int x=sum[lc[v]]-sum[lc[u]];
int y=sum[rc[v]]-sum[rc[u]];
if(l==r)
{
pos=l;
return ;
}
if(x>mm) query(lc[u],lc[v],l,mid,mm);
else if(y>mm) query(rc[u],rc[v],mid+1,r,mm);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
fr(i,1,n) cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
q=unique(b+1,b+n+1)-b-1;
build(rt[0],1,q);
for(int i=1,j=1;i<=n;i++)
{
p=lower_bound(b+1,b+q+1,a[i])-b;
rt[i]=update(rt[i-1],1,q);
}
int l,r;
fr(i,1,m)
{
cin>>l>>r;
int mm=(r-l+1)/2;
pos=0;
query(rt[l-1],rt[r],1,q,mm);
printf("%d\n",b[pos]);
}
return 0;
}
P3919 【模板】可持久化线段树 1(可持久化数组)
思路:一个主席树的模板
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl;
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=1e6+5,INF=1e9;
const double eps=1e-6;
int n,m,a[N],sz=0;
int rt[N*20],lc[N*20],rc[N*20],sum[N*20],val[N*20];
char s[5];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void build(int&rt,int l,int r)
{
rt=++sz;
if(l==r)
{
val[rt]=a[l];
return ;
}
int mid=(l+r)>>1;
build(lc[rt],l,mid);
build(rc[rt],mid+1,r);
}
int update(int o,int l,int r,int x,int k)
{
int oo=++sz;lc[oo]=lc[o],rc[oo]=rc[o];
if(l==r)
{
val[oo]=k;
return oo;
}
int mid=(l+r)>>1;
if(x<=mid) lc[oo]=update(lc[oo],l,mid,x,k);
else rc[oo]=update(rc[oo],mid+1,r,x,k);
return oo;
}
int query(int u,int l,int r,int x)
{
if(l==r)
{
return val[u];
}
int mid=(l+r)>>1;
if(x<=mid) return query(lc[u],l,mid,x);
else return query(rc[u],mid+1,r,x);
}
int main()
{
n=read(),m=read();
fr(i,1,n) a[i]=read();
build(rt[0],1,n);
fr(i,1,m)
{
int v=read(),op=read(),pos=read(),k;
if(op==1)
{
k=read();
rt[i]=update(rt[v],1,n,pos,k);
}
else
{
printf("%d\n",query(rt[v],1,n,pos));
rt[i]=rt[v];
}
}
return 0;
}
P3939 数颜色
思路:题目询问需要在线进行回答,因为询问的只是兔子的颜色,所以只需要修改rt[k]的中的节点即可。
因为若 l<k&&r<k 那么[l,r]的询问区间跟k的修改无关。若l<k&&r>k [l,r]的询问只和个数有关无关位置。若l>k 同样[l,r]询问与k无关。因此只需要修改k的位置。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=3e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,p,q,sz=0,k,ans=0,pos[N];
int a[N],b[N];
int rt[N<<6],lc[N<<6],rc[N<<6],sum[N<<6];
char s[5];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int update(int o,int l,int r,int val)
{
int oo=++sz;
lc[oo]=lc[o],rc[oo]=rc[o],sum[oo]=sum[o]+val;
if(l==r)
return oo;
int mid=(l+r)>>1;
if(p<=mid) lc[oo]=update(lc[oo],l,mid,val);
else rc[oo]=update(rc[oo],mid+1,r,val);
return oo;
}
int query(int u,int v,int l,int r,int mm)
{
int mid=(l+r)>>1;
if(l==r)
return sum[v]-sum[u];
if(mm<=mid) return query(lc[u],lc[v],l,mid,mm);
else return query(rc[u],rc[v],mid+1,r,mm);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
fr(i,1,n) cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
q=unique(b+1,b+n+1)-b-1;
for(int i=1,j=1;i<=n;i++)
{
p=lower_bound(b+1,b+q+1,a[i])-b;
pos[a[i]]=p;
rt[i]=update(rt[i-1],1,q,1);
}
int l,r,op,k;
fr(i,1,m)
{
cin>>op;
if(op==1)
{
cin>>l>>r>>k;
if(!pos[k])
{
printf("0\n");
continue;
}
printf("%d\n",query(rt[l-1],rt[r],1,q,pos[k]));
}
else
{
///询问的是颜色为k的兔子个数和兔子位置无关
///只修改rt[k]即可
cin>>k;
if(a[k]==a[k+1]) continue;
p=lower_bound(b+1,b+q+1,a[k])-b;
rt[k]=update(rt[k],1,q,-1);
p=lower_bound(b+1,b+q+1,a[k+1])-b;
rt[k]=update(rt[k],1,q,1);
swap(a[k],a[k+1]);
}
}
return 0;
}
2021牛客暑期多校训练营9 E题
思路:从首都到其他城市可看作一棵树,父节点的温度比子节点高,因此先用dfs记录每个节点子树范围,倍增祖先节点。每次查询时找到温度<r的祖先节点,然后在祖先节点进行查询子树范围内符合温度 L<=t&&t<=R的节点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=5e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,k,ans=0,cnt=0,tot=0,sz=0;
int a[N],pos[N],dfn[N],edn[N],fa[N][21],rt[N];
int lc[N*20],rc[N*20],sum[N*20];
char s[5];
vector<int>h[N];
struct node
{
int l,r,val;
}b[N*4];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void dfs(int x,int f) ///求出子树的起点和终点
{
dfn[x]=++cnt;
pos[cnt]=x;
fa[x][0]=f;
fr(i,1,20) fa[x][i]=fa[fa[x][i-1]][i-1];///倍增祖父节点
fr(i,0,h[x].size()-1)
{
int y=h[x][i];
if(y==f) continue;
dfs(y,x);
}
edn[x]=cnt;
}
void update(int u,int &v,int l,int r,int pos)
{
v=++sz;
lc[v]=lc[u],rc[v]=rc[u],sum[v]=sum[u]+1;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) update(lc[u],lc[v],l,mid,pos);
else update(rc[u],rc[v],mid+1,r,pos);
}
int query(int u,int l,int r,int L,int R)
{
if(u==0) return 0;
if(L<=l&&r<=R)
{
return sum[u];
}
if(l>R||r<L) return 0;
int mid=(l+r)>>1;
return query(lc[u],l,mid,L,R)+query(rc[u],mid+1,r,L,R);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
fr(i,1,n-1)
{
int x,y;
cin>>x>>y;
h[x].push_back(y);
h[y].push_back(x);
}
fr(i,1,n) cin>>a[i];
dfs(1,0);
fr(i,1,n)
update(rt[i-1],rt[i],1,1e9,a[pos[i]]);
m=read();
fr(i,1,m)
{
int x,l,r;
cin>>x>>l>>r;
if(a[x]>r||a[x]<l)
{
puts("0");
continue;
}
fo(i,0,20)
if(a[fa[x][i]]<=r&&fa[x][i])
x=fa[x][i];
printf("%d\n",query(rt[edn[x]],1,1e9,l,r)-query(rt[dfn[x]-1],1,1e9,l,r));
}
return 0;
}
P1383 高级打字机
思路:还是一道主席树的板子题,题目比较清楚的告诉了这题需要访问历史版本,因此需要可持久化数据结构,因此用主席树。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=1e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
ll n,m,k,ans=0,pos=0,sz=0;
int rt[N<<5],lc[N<<5],rc[N<<5],len[N<<5];
char s[5],ch,val[N<<5];
struct node
{
int l,r;
char ch;
}b[N*4];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void update(int&u,int v,int l,int r,char ch)
{
u=++sz;
lc[u]=lc[v],rc[u]=rc[v];
if(l==r)
{
val[u]=ch;
//cout<<u<<" "<<val[u]<<endl;
return ;
}
int mid=(l+r)>>1;
if(len[pos]<=mid) update(lc[u],lc[v],l,mid,ch);
else update(rc[u],rc[v],mid+1,r,ch);
}
char query(int u,int l,int r,int pos)
{
if(l==r)
return val[u];
int mid=(l+r)>>1;
if(pos<=mid) return query(lc[u],l,mid,pos);
else return query(rc[u],mid+1,r,pos);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
fr(i,1,n)
{
int k;
cin>>s;
if(s[0]=='T')
{
cin>>ch;
++pos;
len[pos]=len[pos-1]+1;
update(rt[pos],rt[pos-1],1,n,ch);
}
else
{
cin>>k;
if(s[0]=='U')
{
++pos;
rt[pos]=rt[pos-k-1];
len[pos]=len[pos-k-1];
}
else
cout<<query(rt[pos],1,n,k)<<endl;
}
}
return 0;
}
P2633 Count on a tree
思路:LCA+主席树+树上差分
u->v路径上的节点数:sum[rt[u]]+sum[rt[v]]-sum[LCA]-sum[fa[LCA]]
主席树记录节点u到祖先节点路径上各个点的权值。
板子都很熟悉,只不过思路一直跳不开怎么去创建只有祖先节点的主席树,那么就在建树的时候同时更新就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl;
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=1.2e5+5,INF=1e9;
const double eps=1e-6;
int n,m,sz=0,p,q,t=30,last=0,ans=0;
int rt[N<<5],lc[N<<5],rc[N<<5],sum[N<<5];
int a[N],b[N],fa[N][32],d[N];
char s[5];
vector<int>h[N];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int update(int o,int l,int r,int p)
{
int oo=++sz;
lc[oo]=lc[o],rc[oo]=rc[o];sum[oo]=sum[o]+1;
if(l==r)
return oo;
int mid=(l+r)>>1;
if(p<=mid) lc[oo]=update(lc[oo],l,mid,p);
else rc[oo]=update(rc[oo],mid+1,r,p);
return oo;
}
int query(int u,int v,int l,int r,int k,int lca,int gra)
{
int x=sum[lc[v]]+sum[lc[u]]-sum[lc[lca]]-sum[lc[gra]];
if(l==r)
return l;
int mid=(l+r)>>1;
if(k<=x) return query(lc[u],lc[v],l,mid,k,lc[lca],lc[gra]);
else return query(rc[u],rc[v],mid+1,r,k-x,rc[lca],rc[gra]);
}
void dfs(int x,int f)
{
d[x]=d[f]+1;
for(int i=0;i<h[x].size();i++)
{
int y=h[x][i];
if(y==f) continue;
fa[y][0]=x;
rt[y]=update(rt[x],1,q,a[y]);
dfs(y,x);
}
}
int LCA(int x,int y)
{
if(d[x]>d[y]) swap(x,y);
for(int j=t;j>=0;j--)
if(d[fa[y][j]]>=d[x]) y=fa[y][j];
if(x==y) return x;
for(int j=t;j>=0;j--)
if(fa[x][j]!=fa[y][j])
x=fa[x][j],y=fa[y][j];
return fa[x][0];
}
int main()
{
n=read(),m=read();
t=log2(n)+1;
fr(i,1,n) a[i]=read(),b[i]=a[i];
sort(b+1,b+n+1);
q=unique(b+1,b+n+1)-b-1;
fr(i,1,n) a[i]=lower_bound(b+1,b+q+1,a[i])-b;
fr(i,1,n-1)
{
int x=read(),y=read();
h[x].push_back(y);
h[y].push_back(x);
}
rt[1]=update(rt[0],1,q,a[1]);
dfs(1,0);
for(int i=1;i<=t;i++) ///倍增祖父节点
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
fr(i,1,m)
{
int l=read()^last,r=read(),k=read();
int lca=LCA(l,r);
ans=b[query(rt[l],rt[r],1,q,k,rt[lca],rt[fa[lca][0]])];
printf("%d\n",ans);
last=ans;
}
return 0;
}
too the moon
思路:题目要求访问历史版本,所以利用主席树写。一个带修的主席树模板。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=1e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,k,ans=0,q,sz=0,now=0;
int rt[N<<5],lc[N<<5],rc[N<<5],a[N];
ll sum[N<<5],add[N<<5];
char s[5];
void build(int &rt,int l,int r)
{
rt=++sz;
add[sz]=0;
if(l==r)
{
sum[rt]=a[l];
return ;
}
int mid=(l+r)>>1;
build(lc[rt],l,mid);
build(rc[rt],mid+1,r);
sum[rt]=sum[lc[rt]]+sum[rc[rt]];
}
int update(int o,int l,int r,int L,int R,ll k)
{
int oo=++sz;
lc[oo]=lc[o],rc[oo]=rc[o],sum[oo]=sum[o],add[oo]=add[o];
sum[oo]+=1ll*(R-L+1)*k;
if(L<=l&&r<=R)
{
add[oo]+=k;
return oo;
}
int mid=(l+r)>>1;
if(R<=mid) lc[oo]=update(lc[oo],l,mid,L,R,k);
else if(L>mid) rc[oo]=update(rc[oo],mid+1,r,L,R,k);
else
{
lc[oo]=update(lc[oo],l,mid,L,mid,k);
rc[oo]=update(rc[oo],mid+1,r,mid+1,R,k);
}
return oo;
}
ll query(int u,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return sum[u];
int mid=(l+r)>>1;
ll ans=add[u]*1ll*(R-L+1);
if(R<=mid) ans+=query(lc[u],l,mid,L,R);
else if(L>mid) ans+=query(rc[u],mid+1,r,L,R);
else
{
ans+=query(lc[u],l,mid,L,mid);
ans+=query(rc[u],mid+1,r,mid+1,R);
}
return ans;
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>m)
{
sz=0;now=0;
fr(i,1,n) cin>>a[i];
build(rt[0],1,n);
fr(i,1,m)
{
int l,r,t;
ll k;
cin>>s;
if(s[0]=='B')
{
cin>>t;
now=t;
}
else
{
cin>>l>>r;
if(s[0]=='C')
{
cin>>k;
++now;
rt[now]=update(rt[now-1],1,n,l,r,k);
}
if(s[0]=='H')
{
cin>>t;
cout<<query(rt[t],1,n,l,r)<<endl;
}
if(s[0]=='Q')
cout<<query(rt[now],1,n,l,r)<<endl;
}
}
}
return 0;
}
P2617 Dynamic Rankings
思路:树状数组套主席树。如果单次修改[l,n]所有的主席树时间复杂度将是O(nlogn),会TLE。因此选择修改O(logn)个主席树,主席树思想就是前缀和,因此利用树状数组修改logn个主席树,并对[l-1,r] 的树查询。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=4e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,q,sz=0,n1,n2,tot;
int rt[N],lc[N<<5],rc[N<<5],val[N<<5];
int a[N],b[N<<1],t1[N],t2[N];
char s[5],ch;
struct node
{
int l,r,k;
}que[N];
int lowbit(int x) {return x&-x;}
void add(int &rt,int l,int r,int pos,int k)
{
if(!rt) rt=++sz;
val[rt]+=k;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) add(lc[rt],l,mid,pos,k);
else add(rc[rt],mid+1,r,pos,k);
}
void change(int pos,int k)
{
int x=lower_bound(b+1,b+q+1,a[pos])-b;
for(int i=pos;i<=n;i+=lowbit(i)) ///修改logn个主席树
add(rt[i],1,q,x,k);
}
int Kth(int l,int r,int k) ///查询[l,r] 第k大的数
{
if(l==r) return l;
int mid=(l+r)>>1,sum=0;
fr(i,1,n2) sum+=val[lc[t2[i]]];
fr(i,1,n1) sum-=val[lc[t1[i]]];
if(sum>=k)
{
fr(i,1,n1) t1[i]=lc[t1[i]];
fr(i,1,n2) t2[i]=lc[t2[i]];
return Kth(l,mid,k);
}
else
{
fr(i,1,n1) t1[i]=rc[t1[i]];
fr(i,1,n2) t2[i]=rc[t2[i]];
return Kth(mid+1,r,k-sum);
}
}
int Kth_pre(int l,int r,int k)
{
n1=n2=0;
for(int i=l-1;i>=1;i-=lowbit(i)) ///树状数组查询logn个主席树
t1[++n1]=rt[i];
for(int i=r;i>=1;i-=lowbit(i))
t2[++n2]=rt[i];
return Kth(1,q,k);
}
int main()
{
int T=1;
while(T--)
{
cin>>n>>m;
sz=0;tot=n;
memset(rt,0,sizeof(rt));
memset(lc,0,sizeof(lc));
memset(rc,0,sizeof(rc));
memset(val,0,sizeof(val));
fr(i,1,n) cin>>a[i],b[i]=a[i];
memset(que,0,sizeof(que));
fr(i,1,m)
{
cin>>ch;
if(ch=='C')
{
cin>>que[i].l>>que[i].r;
b[++tot]=que[i].r;
}
if(ch=='Q')
cin>>que[i].l>>que[i].r>>que[i].k;
}
sort(b+1,b+tot+1);
q=unique(b+1,b+tot+1)-b-1;
fr(i,1,n) change(i,1);
fr(i,1,m)
{
if(que[i].k)
cout<<b[Kth_pre(que[i].l,que[i].r,que[i].k)]<<endl;
else
{
change(que[i].l,-1);
a[que[i].l]=que[i].r;
change(que[i].l,1);
}
}
}
return 0;
}