正题
题目链接:https://www.luogu.com.cn/problem/P2617
题目大意
给出一个序列,要求支持
- 区间查询第kkk大
- 单点修改
解题思路
区间查询第kkk大需要使用主席树,构建权值线段树的前缀和。考虑如何进行单点修改,在前缀和上进行单点修改就是进行区间修改,区间修改主席树显然不可能,考虑树套树。
我们可以维护以权值线段树为权值的树状数组,这样可以单点修改并且维护动态前缀和。
时间复杂度:O(nlog2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define lowbit(x) (x&-x)
using namespace std;
const int N=1e5+10,M=N*400;
int n,m,a[N],b[N*2],root[N];
int q1[N],q2[N],q3[N],num;
int w[M],ls[M],rs[M],tot;
int cnt1,cnt2,tmp1[N],tmp2[N];
char op[N];
void Updata(int &x,int l,int r,int pos,int val){if(!x)x=++num;if(l==r){w[x]+=val;return;}int mid=(l+r)>>1;if(pos<=mid)Updata(ls[x],l,mid,pos,val);else Updata(rs[x],mid+1,r,pos,val);w[x]=w[ls[x]]+w[rs[x]];
}
void Change(int x,int val){int k=lower_bound(b+1,b+1+tot,a[x])-b;while(x<=n){Updata(root[x],1,tot,k,val);x+=lowbit(x);}return;
}
int Ask(int l,int r,int k){if(l==r)return l;int mid=(l+r)>>1,sum=0;for(int i=1;i<=cnt1;i++)sum-=w[ls[tmp1[i]]];for(int i=1;i<=cnt2;i++)sum+=w[ls[tmp2[i]]];if(k<=sum){for(int i=1;i<=cnt1;i++)tmp1[i]=ls[tmp1[i]];for(int i=1;i<=cnt2;i++)tmp2[i]=ls[tmp2[i]];return Ask(l,mid,k);}else{for(int i=1;i<=cnt1;i++)tmp1[i]=rs[tmp1[i]];for(int i=1;i<=cnt2;i++)tmp2[i]=rs[tmp2[i]];return Ask(mid+1,r,k-sum);}
}
int Query(int x,int l,int r){l--;cnt1=cnt2=0;for(int i=l;i;i-=lowbit(i))tmp1[++cnt1]=root[i];for(int i=r;i;i-=lowbit(i))tmp2[++cnt2]=root[i];return Ask(1,tot,x);
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[++tot]=a[i];}for(int i=1;i<=m;i++){cin>>op[i];scanf("%d %d",&q1[i],&q2[i]);if(op[i]=='Q')scanf("%d",&q3[i]);else b[++tot]=q2[i];}sort(b+1,b+1+tot);tot=unique(b+1,b+1+tot)-b-1;for(int i=1;i<=n;i++)Change(i,1);for(int i=1;i<=m;i++){if(op[i]=='Q')printf("%d\n",b[Query(q3[i],q1[i],q2[i])]);else{Change(q1[i],-1);a[q1[i]]=q2[i];Change(q1[i],1);}}return 0;
}