*点击

[TJOI2010] 中位数

题目

Description

给定一个由 NN 个元素组成的整数序列,现在有两种操作:

  • 1 add a1 add a:在该序列的最后添加一个整数 aa,组成长度为 N+1N+1 的整数序列。
  • 2 mid2 mid:输出当前序列的中位数。

中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)

例 11:[1,2,13,14,15,16][1,2,13,14,15,16] 中位数为 1313。
例 22:[1,3,5,7,10,11,17][1,3,5,7,10,11,17] 中位数为 77。
例 33:[1,1,1,2,3][1,1,1,2,3] 中位数为 11。

Input

第一行为初始序列长度 NN。第二行为 NN 个整数,表示整数序列,数字之间用空格分隔。第三行为操作数 MM,即要进行 MM 次操作。下面为 MM 行,每行输入格式如题意所述。

Output

对于每个 midmid 操作输出中位数的值。

Sample Input

6
1 2 13 14 15 16
5
add 5
add 3
mid
add 20
mid

Sample Output

5
13

思路

查找中位数;

一共有四种方法,平衡树,二分+树状数组,权值线段树,堆;

 因为树状数组和线段树写法需要离散化,要存下所有加入的数;

作者太懒,所以只介绍平衡树和堆的写法;

平衡树就是板子所以就不细说了;

 

堆的写法就是维护一个大根堆和一个小根堆;

查询第k个数时,大根堆表示前k减一个数;

那么答案就是小根堆的堆顶元素;

维护过程只需保证大根堆中所有元素都比小根堆小即可;

因为每次询问的都是中位数,

所以插入时,若插入前元素个数为偶数,则插入大根堆;

若插入前元素个数为奇数,则插入小根堆;


 

 

代码

平衡树写法

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll _=2e5+1;
ll n,m;
ll root,tot;
ll size[_];
struct node
{
    ll l,r,v,f;
}a[_];
inline void update(ll p)
{
    size[p]=size[a[p].l]+size[a[p].r]+1;
}
inline ll build(ll v)
{
    a[++tot].v=v;
    a[tot].f=rand();
    size[tot]=1;
    return tot;
}
void split(ll p,ll k,ll &x,ll &y)
{
    if(p==0)
    {
        x=y=0;
        return;
    }
    if(a[p].v<=k)
    {
        x=p;
        split(a[p].r,k,a[p].r,y);
    }
    else
    {
        y=p;
        split(a[p].l,k,x,a[p].l);
    }
    update(p);
}
ll merge(ll x,ll y)
{
    if(x==0||y==0)
        return x+y;
    if(a[x].f<a[y].f)
    {
        a[x].r=merge(a[x].r,y);
        update(x);
        return x;
    }
    else
    {
        a[y].l=merge(x,a[y].l);
        update(y);
        return y;
    }
}
void insert(ll v)
{
    ll x,y;
    split(root,v-1,x,y);
    root=merge(merge(x,build(v)),y);
}
ll findk(ll k)
{
    ll p=root;
    while(1)
    {
        if(k==size[a[p].l]+1)
            return a[p].v;
        if(k<size[a[p].l]+1)
            p=a[p].l;
        if(k>size[a[p].l]+1)
        {
            k-=size[a[p].l]+1;
            p=a[p].r;
        }
    }
}
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        ll x;
        scanf("%lld",&x);
        insert(x);
    }
    scanf("%lld",&m);
    for(ll i=1;i<=m;i++)
    {
        string opt;
        cin>>opt;
        if(opt=="add")
        {
            ll x;
            scanf("%lld",&x);
            n++;
            insert(x);
        }
        else
            printf("%lld\n",findk((n+1)/2));
    }
    return 0;
}

 

堆写法

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll _=2e5+1;
ll n,m;
ll a[_];
priority_queue<ll> q,qq;//q表示小根堆,qq为大根堆
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(ll i=1;i<n/2;i++)
        qq.push(a[i]);
    for(ll i=n/2;i<=n;i++)
        q.push(-a[i]);//大根堆里插入这个数的相反数就可以实现小根堆
    scanf("%lld",&m);
    for(ll i=1;i<=m;i++)
    {
        string opt;
        cin>>opt;
        if(opt=="add")
        {
            ll x;
            scanf("%lld",&x);
            if(n%2==0) qq.push(x);
            else q.push(-x);//插入
            n++;
        }
        else
        {
            while(!q.empty()&&!qq.empty()&&-q.top()<qq.top())
            {//维护保证大根堆中每一个元素都比小根堆要小
                ll x=-q.top(),xx=qq.top();
                q.pop(); qq.pop();
                q.push(-xx); qq.push(x);
            }
            printf("%lld\n",-q.top());
        }
    }
    return 0;
}

 

 

 

 

 

posted @ 2024-12-10 16:54  木偶人-怪咖  阅读(6)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录