P1168 中位数(权值线段树)
前言
在一场著名的名叫 SCP2021 的考试中,本雪使用对顶堆险些切掉了 T1(只是第一个堆的重载小于号的标准写错了)。而卷王苏奆奆却祭出了一个名叫权值线段树的 so-called advanced 数据结构(因为没有排序痛失切题良机),让本雪无比倾心,在经过了漫长的岁月之后,阅尽沧桑,本雪终于来学习了这个并不太难的数据结构。
传送门:权值线段树
Sol
本题就是一个类似于静态区间维护排名为 k 的值(只不过特殊化成了中位数)。权值线段树,顾名思义,就是把权值的数量建成一棵线段树,每个节点表示的是权值在该节点的区间范围内的数的数量(非常好理解的)。例如,区间 \([3,5]\) 表示的就是权值为 3,4,5 的数的数量。然后这个题,根据数据范围 \(a_i\leq 10^9\),对其进行离散化之后,直接建一棵权值线段树,然后单点修改,区间查询就可以啦。区间查询的时候如果是树状数组需要二分,而权值线段树本身就具有区间二分的性质,直接不断向下到左儿子区间或右儿子区间寻找即可。时间复杂度 \(O(nlogn)\)。(注:本题也可以使用对顶堆,请自行尝试。)
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 100000 + 10;
int n,tot;
int tree[4*N],a[N],p[N];
void pushup(int p)
{
tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
void change(int p,int l,int r,int k)
{
if(l == r)
{
tree[p]++;
return ;
}
int mid = (l + r) >> 1;
if(mid >= k) change(p << 1,l,mid,k);
else change(p << 1 | 1,mid + 1,r,k);
pushup(p);
}
int query(int p,int l,int r,int k)
{
if(l == r) return l;
int mid = (l + r) >> 1;
if(tree[p << 1] >= k) return query(p << 1,l,mid,k);
else return query(p << 1 | 1,mid + 1,r,k - tree[p << 1]);
}
void dec()
{
sort(a + 1,a + n + 1);
tot = unique(a + 1,a + n + 1) - a - 1;
for(int i = 1;i <= n;i++)
p[i] = lower_bound(a + 1,a + tot + 1,p[i]) - a;
}
signed main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
p[i] = a[i];
}
dec();
for(int i = 1;i <= n;i++)
{
change(1,1,tot,p[i]);
if(i & 1) printf("%d\n",a[query(1,1,tot,(i + 1) / 2)]);
}
return 0;
}

浙公网安备 33010602011771号