Description
给出一个长度为n的序列a,两种操作
C x v:将第x个元素的值改成v
Q l r k:查询区间[l,r]中第k大的元素
Input
第一行为一个整数t表示用例组数,每组用例第一行为两个整数n和m分别表示序列长度和操作数,第二行n个整数表示序列a,之后m行每行一种操作
(0< t<=4,1<=n<=50000,1<=m<=10000)
Output
对于每次查询,输出查询结果
Sample Input
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
3
6
Solution
主席树动态第k大,设序列与操作中不同的数有s个,如果完全使用动态主席树,空间复杂度为O((n+m)*log n*log s),在这道题的内存限制下开不下这么大的数组,所以需要建两种主席树,第一种就是静态主席树维护每个前缀序列,第二种的动态主席树维护修改操作,每次查询时对这两棵树分别查询然后做和即可,这样的空间复杂度为O(nlog s+m*log n*log s)
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 50005
#define lowbit(x) (x&(-x))
struct Tree
{
int left,right,data;
}T[50*maxn];
int t,n,m,a[maxn],b[2*maxn],pos[maxn],root[2*maxn],L[maxn],R[maxn],K[maxn],tot,cnt,res;
char O[maxn];
vector<int>p,q;
void build(int &i,int x,int l,int r)
{
T[++tot]=T[i];
i=tot;
T[i].data++;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) build(T[i].left,x,l,mid);
else build(T[i].right,x,mid+1,r);
}
void insert(int &i,int x,int y,int l,int r)
{
T[++tot]=T[i];
i=tot;
T[i].data+=y;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) insert(T[i].left,x,y,l,mid);
else insert(T[i].right,x,y,mid+1,r);
}
void bit_insert(int i,int x,int y)
{
while(i<=n)
{
insert(root[i],x,y,1,res);
i+=lowbit(i);
}
}
int query(int k,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1,cnt1=0,cnt2=0;
for(int i=0;i<p.size();i++) cnt1+=T[T[p[i]].left].data;
for(int i=0;i<q.size();i++) cnt2+=T[T[q[i]].left].data;
if(cnt2-cnt1>=k)
{
for(int i=0;i<p.size();i++) p[i]=T[p[i]].left;
for(int i=0;i<q.size();i++) q[i]=T[q[i]].left;
return query(k,l,mid);
}
else
{
for(int i=0;i<p.size();i++) p[i]=T[p[i]].right;
for(int i=0;i<q.size();i++) q[i]=T[q[i]].right;
return query(k-(cnt2-cnt1),mid+1,r);
}
}
int solve(int l,int r,int k)
{
p.clear();q.clear();
if(l>0) p.push_back(root[l+n]);//若l=0则不能将root[n]加到p中
q.push_back(root[r+n]);
while(l>0)
{
p.push_back(root[l]);
l-=lowbit(l);
}
while(r>0)
{
q.push_back(root[r]);
r-=lowbit(r);
}
return query(k,1,res);
}
int main()
{
scanf("%d",&t);
while(t--)
{
tot=cnt=0;
memset(root,0,sizeof(root));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[++cnt]=a[i];
for(int i=1;i<=m;i++)
{
char op[11];
scanf("%s%d%d",op,&L[i],&R[i]);O[i]=op[0];
if(op[0]=='Q') scanf("%d",&K[i]);
else b[++cnt]=R[i];
}
sort(b+1,b+cnt+1);
res=unique(b+1,b+cnt+1)-b-1;//利用去重函数得到节点数
for(int i=1;i<=n;i++) pos[i]=lower_bound(b+1,b+res+1,a[i])-b;//pos记录每个值在主席树中的位置
for(int i=1;i<=n;i++)
{
root[i+n]=root[i+n-1];
build(root[i+n],pos[i],1,res);//建主席树
}
for(int i=1;i<=m;i++)
{
if(O[i]=='C')
{
bit_insert(L[i],pos[L[i]],-1);//消除原先值的影响
pos[L[i]]=lower_bound(b+1,b+res+1,R[i])-b;//更新pos值
bit_insert(L[i],pos[L[i]],1);//加入更新值的影响
}
else
{
int ans=solve(L[i]-1,R[i],K[i]);
printf("%d\n",b[ans]);
}
}
}
return 0;
}