我的思路和大部分题解一样,都是先把a数组存下来,然后排序去重,记录到b数组里,然后用b数组建一颗线段树。但是具体实现上略微有些不同。
线段树数组存的是它维护区间的左端点(也就是区间最小值)\(l\)和右端点\(r\)(区间最大值)以及本身和子树所存的数字的个数\(num\)。
这样当我们add的时候,如果插入的数x比左子树的右端点大,就去找右子树,找到对应位置后,就将\(num++\)。
再查找中位数的时候,如果\(rank<=\)当前节点的\(num\),就去查找左子树,\(else\),就将\(rank-\)左子树的\(num\),让后去查右子树。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ls p<<1 #define rs p<<1|1 #define mid ((l+r)>>1) using namespace std; int a[100010],b[100010]; //======线段树及其相关操作 struct zzz{ int num,l,r; }tree[100010<<2]; inline void up(int p){ tree[p].num=tree[ls].num+tree[rs].num; //维护当前节点所存的数字个数 tree[p].l=tree[ls].l; tree[p].r=tree[rs].r; //维护左右端点 } void build(int l,int r,int p){ //建树 if(l==r){ tree[p].r=tree[p].l=b[l]; return ; } build(l,mid,ls); build(mid+1,r,rs); up(p); } void update(int p,int k){ //单点修改 if(tree[p].l==tree[p].r&&tree[p].l==k){ //如果找到对应位置 tree[p].num++; //它存的数字个数++ return ; } if(k>tree[ls].r) update(rs,k); //如果插入的数x比左子树的右端点大,就去找右子树 else update(ls,k); //else,就去找左子树 up(p); } int ans(int l,int r,int p,int rank){ //查找中位数 int zzz=0; if(l==r) return tree[p].l; if(rank<=tree[ls].num) //如果k<=当前节点的num zzz=ans(l,mid,ls,rank); //查找左子树 else{ rank-=tree[ls].num; //否则,将rank-左子树的的num zzz=ans(mid+1,r,rs,rank); //去找右子树 } return zzz; } //=======快读 int read(){ int k=0; char c=getchar(); for(;c<'0'||c>'9';) c=getchar(); for(;c>='0'&&c<='9';c=getchar()) k=(k<<3)+(k<<1)+c-48; return k; } int main(){ int n=read(); for(int i=1;i<=n;i++) b[i]=a[i]=read(); sort(b+1,b+n+1); //排序 int sz=unique(b+1,b+n+1)-(b+1); //去重 build(1,sz,1); for(int i=1;i<=n;i++){ update(1,a[i]); if(i&1) printf("%d\n",ans(1,sz,1,(i+1)/2)); } return 0; }