小H前段时间在面试,面试官问了一个中位数的问题:一个长度为n的序列A, 怎么求A所有前缀子串的中位数。小H当时想破头也没想到,所以今天不得不向你求助。可是前缀子串有时候太多了,因此,小H好心帮你减轻工作量,你只用求长度为奇数的前缀子串的中位数。
输入
第一行为n,表示序列长度;随后的n个自然数。
(1e3<=n<=1e5, 1<=a[i]<=1e9)
输出
输出(n + 1)/2个数,分别是子串[0,0],[0,2],[0,4]…的中位数。
样例输入
12
16 23 28 19 9 24 27 10 22 6 15 1
样例输出
16
23
19
23
22
19
两种解法
对顶堆,维护两个优先队列,第一个大根堆q1,第二个小根堆q2,第二个为空时候或者当前要插入的值大于小根堆顶元素的时候,插入第二个堆,否则进入第一个堆。另外满足
1. q1.size()<=q2.size()
2. q2.size()<=q1.szie()+1
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
const int inf=0x3f3f3f3f;
int a[maxn];
priority_queue<ll>q1;
priority_queue<ll,vector<ll>,greater<ll>>q2;
void insert(ll x)
{
if(!q2.size()||x>q2.top())q2.push(x);
else q1.push(x);
if(q1.size()>q2.size())q2.push(q1.top()),q1.pop();
if(q2.size()>q1.size()+1)q1.push(q2.top()),q2.pop();
}
int main()
{
ll n,x;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
insert(x);
if(i&1)
{
printf("%lld\n",q2.top());
}
}
return 0;
}
另外一个写法就是维护一个可持久化线段树(主席树),模板题,找第k大。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int a[maxn];
int rt[maxn];
struct node
{
int l,r;
int sum;
}T[maxn*20];
int tot=0;//结点编号
vector<int> v;
int getid(int k)
{
return lower_bound(v.begin(),v.end(),k)-v.begin()+1;
}
void build(int &o,int l,int r)
{
o=++tot;
T[o].sum=0;
if(l==r) return;
int mid=(l+r)/2;
build(T[o].l,l,mid);
build(T[o].r,mid+1,r);
}
void update(int l,int r,int &now,int last,int k)
{
T[++tot]=T[last];//复制线段树
//更新当前线段树的根结点
now=tot;
T[tot].sum++;
if(l==r) return;//修改到叶子结点为止
//根据需要修改的k来确定是修改左子树还是修改右子树
int mid=(l+r)/2;
if(k<=mid)
update(l,mid,T[now].l,T[last].l,k);
else
update(mid+1,r,T[now].r,T[last].r,k);
}
int query(int l,int r,int x,int y,int k)//查询区间【x,y】中第小的数
{
if(l==r) return l;//查询到叶子结点为止
int mid=(l+r)/2;
int cnt=T[T[y].l].sum-T[T[x].l].sum;//第y颗树比第x颗树在左子树上多的结点数
if(cnt>=k)//答案在左子树上
return query(l,mid,T[x].l,T[y].l,k);
else
return query(mid+1,r,T[x].r,T[y].r,k-cnt);
}
int main()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
//build(,1,n);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
build(rt[0],1,n);
for(int i=1;i<=n;i++)
update(1,n,rt[i],rt[i-1],getid(a[i]));
for(int i=0;i<n;i+=2)
{
int x,y,k;
x=1,y=i+1,k=(x+y)/2;
printf("%d\n",v[query(1,n,rt[x-1],rt[y],k)-1]);
}
return 0;
}