正题
这道题我理解了半天,网上好的题解我没有看懂,所以在这里尽量写得详细简略一些。
动态第k大要了解的是两个东西。
一个是树状数组的概念,一个是主席树(动态开点线段树)。
先讲树状数组;
1.定义一个点i维护的信息是[i-lowbit(i)+1,i].lowbit()这个函数的意义是i在二进制下末尾零和倒数第一个数组成的数。像lowbit(7) = lowbit( (111)2 ) = (1)2 = 1 。2指的是二进制下。
2.那么如何扯到这个题上面去呢?
再讲主席树。
1.在求静态第k大的过程中,我们习惯于用前缀和主席树来处理区间问题,然后利用“每次只加一条链”的性质节省空间。
2.但是在此题中,如果还是用根来表示位置,用底层主席树维护的值来表示权值的话,那么修改必将很麻烦。
3.因为在这时,更改当前点x的值是会影响到x~n的主席树的构成。
所以我们在这里提出——用树状数组套一套
我们现在建出来的主席树,第i个根维护的是[i-lowbit(i)+1,i]区间的信息,而不是像以前的前缀树一样。但是很多人想问,前缀树有自己优化空间的方法,树状数组套主席树有自己的优化方法吗?
没有
所以我们要暴力开空间
void update(int &now,int l,int r){//这里的now是引用,表示上一层的左儿子或右儿子
if(now==0) now=++tot;//我还没有这个儿子,新建出来
c[now]+=d;//加上一个d,d在增加的时候为1,减的时候为0
if(l==r) return ;
if(v<=(l+r)/2) update(ls[now],l,(l+r)/2);//在左边,往左边找
else update(rs[now],(l+r)/2+1,r);//在右边,往右边找
}
代码理解好了,什么都没有事。
最后要看的还是全部。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int a[10010];
int n,m;
int root[10010],ls[2600010],rs[2600010],c[2600010];
int tot=0;
int xx[30],yy[30];
int v,d;
int lowbit(int x){
return x&-x;
}
void update(int &now,int l,int r){
if(now==0) now=++tot;
c[now]+=d;
if(l==r) return ;
if(v<=(l+r)/2) update(ls[now],l,(l+r)/2);
else update(rs[now],(l+r)/2+1,r);
}
void change(){
int x,b;
scanf("%d %d",&x,&b);
d=-1;v=a[x];
for(int i=x;i<=n;i+=lowbit(i))//把原来的删掉
update(root[i],0,1e9);
d=1;v=b;
for(int i=x;i<=n;i+=lowbit(i))//把新的加上
update(root[i],0,1e9);
a[x]=b;
}
int query(){
int x,y,k;
scanf("%d %d %d",&x,&y,&k);
x--;
swap(x,y);
int t1=0,t2=0;
for(int i=x;i>=1;i-=lowbit(i)) xx[++t1]=root[i];//记住,这里是log个根一起跑
for(int i=y;i>=1;i-=lowbit(i)) yy[++t2]=root[i];
int l=0,r=1e9;//二分模拟边界变化
while(l<r){
int temp=0;//算出左儿子
for(int i=1;i<=t1;i++) temp+=c[ls[xx[i]]];
for(int i=1;i<=t2;i++) temp-=c[ls[yy[i]]];
if(k<=temp){//在左儿子,往左走,全部往左边跳
for(int i=1;i<=t1;i++) xx[i]=ls[xx[i]];
for(int i=1;i<=t2;i++) yy[i]=ls[yy[i]];
r=(l+r)/2;
}
else {//在右边,全部往右边跳
for(int i=1;i<=t1;i++) xx[i]=rs[xx[i]];
for(int i=1;i<=t2;i++) yy[i]=rs[yy[i]];
k-=temp;l=(l+r)/2+1;//记住在右边找的是第k-temp个
}
}
return l;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&v);
a[i]=v;d=1;
for(int j=i;j<=n;j+=lowbit(j))//对于每个值,加入时要像树状数组一样跳
update(root[j],0,1e9);
}
char t[2];
while(m--){
scanf("%s",t);
if(t[0]=='Q') printf("%d\n",query());
else change();
}
}