zzulioj 2662: 小H的面试难题

题目传送门

小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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值