C96 树状数组套权值线段树 P2617 Dynamic Rankings
视频链接:C96 树状数组套权值线段树 P2617 Dynamic Rankings_哔哩哔哩_bilibili
C50【模板】可持久化线段树(主席树)P3834 静态区间第 k 小
C84 树状数组套权值线段树 P3157 [CQOI2011] 动态逆序对
Luogu P2617 Dynamic Rankings
// 树状数组 套 权值线段树树 O(nlognlogn) #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=100005; #define lowb(x) x&-x #define mid ((l+r)>>1) int n,m,num,a[N],b[N<<1],c[N],d[N],e[N]; int tot,rt[N],sum[N*400],ls[N*400],rs[N*400]; int n1,n2,rt1[N],rt2[N]; //记录查询路径上的logn个根 void change2(int &u,int l,int r,int p,int k){ //内修 if(!u) u=++tot; sum[u]+=k; if(l==r) return; if(p<=mid) change2(ls[u],l,mid,p,k); else change2(rs[u],mid+1,r,p,k); } void change1(int x,int k){ //外修 int p=lower_bound(b+1,b+1+num,a[x])-b; while(x<=n) change2(rt[x],1,num,p,k), x+=lowb(x); } int query2(int l,int r,int k){ //内查 if(l==r) return l; int s=0; // 计算两条查询路径上左子树的前缀和之差 for(int i=1;i<=n1;++i) s-=sum[ls[rt1[i]]]; for(int i=1;i<=n2;++i) s+=sum[ls[rt2[i]]]; if(s>=k){ //前缀和之差>=k,说明区间第k小在左子树 // 两条查询路径上的logn个线段树一起走到左子树 for(int i=1;i<=n1;++i) rt1[i]=ls[rt1[i]]; for(int i=1;i<=n2;++i) rt2[i]=ls[rt2[i]]; return query2(l,mid,k); } else{ //前缀和之差 <k,说明区间第k小在右子树 // 两条查询路径上的logn个线段树一起走到右子树 for(int i=1;i<=n1;++i) rt1[i]=rs[rt1[i]]; for(int i=1;i<=n2;++i) rt2[i]=rs[rt2[i]]; return query2(mid+1,r,k-s); } } int query1(int x,int y,int k){ //外查 n1=n2=0; while(x) rt1[++n1]=rt[x],x-=lowb(x); //左路径上的根 while(y) rt2[++n2]=rt[y],y-=lowb(y); //右路径上的根 return query2(1,num,k); } int main(){ scanf("%d%d",&n,&m); char op[5]; for(int i=1;i<=n;++i)scanf("%d",&a[i]),b[++num]=a[i]; for(int i=1;i<=m;++i){ scanf("%s",op); //b数组记录原值a和修改值d if(op[0]=='Q') scanf("%d%d%d",&c[i],&d[i],&e[i]); else scanf("%d%d",&c[i],&d[i]),b[++num]=d[i]; } sort(b+1,b+1+num); //离散化 num=unique(b+1,b+1+num)-b-1; for(int i=1;i<=n;++i) change1(i,1); //建树套树 for(int i=1;i<=m;++i){ if(e[i])printf("%d\n",b[query1(c[i]-1,d[i],e[i])]); else{ change1(c[i],-1); //清除旧值贡献 a[c[i]]=d[i]; //记录修改值 change1(c[i],1); //插入修改值贡献 } } }