luogu P1168 中位数 题解
作者:互联网
这题可以用权值线段树查k-th,可以上平衡树之类
事实上,,这题数据比较水的原因,可以用vector直接水过去。。。
一句话题意:给出一个长度为\(N\)的非负整数序列\(A_i\)
,对于所有\(1≤k≤(N+1)/2\),输出\(A_1,A_3,...,A_{2k-1}\)
的中位数。即前\(1,3,5,…\)个数的中位数。
这里上一个巧妙的线段树做法
首先我们a数组先去重然后存进b数组
然后update的时候记录每个数出现的次数,找到这个数的位置即可。
简单介绍下lower_bound用法:lower_bound(begin,end,x):从数组的begin位置到end-1位置二分查找第一个大于或等于x的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
update:
void update(int p,int w)//w存位置
{
t[p].cnt++;//记录这个数的出现次数
if(t[p].l==t[p].r) return ;
if(w<=t[p].mid) update(p<<1,w);//t[p].mid=l+r>>1,于build()中
else update(p<<1|1,w);
}
query这样写:
int query(int p,int w)//w存位置
//如果节点p的左儿子下有x个数,w>x,
//那么cnt是位于右儿子的第w-x个数。
//否则在左子树寻找第w个。
//这样找下去能保证答案的正确性
//找中位数的巧妙性质
{
if(t[p].l==t[p].r) return t[p].l;
if(cnt<=t[p<<1].cnt) return query(p<<1,cnt);//左子树找
else return query(p<<1|1,cnt-t[p<<1].cnt);//右子树找
}
简单离散化:
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);b[i]=a[i];
}
sort(b+1,b+n+1);
int k=unique(b+1,b+1+n)-b;
简单更新:
for(int i=1;i<=n;i++)
int kk=lower_bound(b+1,b+1+k,a[i])-b,update(1,kk);//找到a[i]在b[]中的位置,并对应更新
然后再输出即可。
//接上面
if(i&1)
printf("%d\n",b[query(1,i/2+1)]);
总结一下
这题还是巧妙在能想到查询的时候在左子树和右子树找。
算是较为灵活的运用。
标签:cnt,end,int,题解,update,中位数,luogu,query,P1168 来源: https://www.cnblogs.com/Ano-Ano/p/12349255.html