权值线段树解KPI(HDU5249)

这几天考试考得心里颇不宁静,于是就来学习一下权值线段树,这是我打的第一道题~

题意:
有以下三种操作:
1:in a 把a放到队里
2:out 取出最先放入的元素
3:query 输出队中元素的中位数
#多组数据
#中位数:n个数字排好序第(n/2+1)个数

思路:
这道题网上许多神犇说是用的set,那么这里主要介绍权值线段树的解法。

首先,要搞清楚权值线段树和普通线段树的区别:权值线段树是根据元素权值排的(有时需离散化),而普通线段树是根据元素编号进行划分;
所以,看得出在权值线段树上找中位数是件很容易的事;
那么问题来了:如何维护一个动态区间的中位数呢?
1:我们记下所有的操作 pre[1~n],顺便存下所有元素(排好序)now[1~sum];
   #sum:in的数量
2:接着,建树 tree[1~maxn<<2]
   #tree[i] 存的是 tree[i] 这个区间中元素个数
2:然后,按照顺序执行操作:
   in(pre[i]>=0):找到插入的数(pre[i])在树上的位置(lower_bound) tmp;
                  在树上tmp位置+1,表示tmp位置上有数;
                  push_up;
   out(pre[i]==-1):找到队首(q.front())在树上的位置 tmp;
                   在树上tmp位置-1,表示tmp位置上的数没了;
                   push_up;
                   q.pop();
   query(pre[i]==-2):寻找在队中间(q.size()/2+1)位置的元素在树上的位置 zz;
                     输出now[zz];

好了,废话不多说,接下来附上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>

#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define ls node<<1
#define rs node<<1|1
#define maxn 100005
using namespace std;
int now[maxn],pre[maxn],tree[maxn<<2];
int Case,n;

void push_up(int node){
    tree[node]=tree[ls]+tree[rs];
}

void build(int node,int l,int r){
    if(l==r){
        tree[node]=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    push_up(node);
}

void updata(int node,int l,int r,int tmp,int flag){
    if(l==r){
        tree[node]+=flag;
        return;
    }
    int mid=(l+r)>>1;
    if(tmp<=mid) updata(ls,l,mid,tmp,flag);
    else updata(rs,mid+1,r,tmp,flag);
    push_up(node);
}

int query(int node,int l,int r,int tmp){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(tree[ls]>=tmp) return query(ls,l,mid,tmp);
    else return query(rs,mid+1,r,tmp-tree[ls]);
}

void work(){
    ++Case;
    printf("Case #%d:\n",Case);
    queue <int> q;
    For(i,1,n){
        char s[10];
        scanf("%s",&s);
        if(s[0]=='i') scanf("%d",&pre[i]);
        else if(s[0]=='o') pre[i]=-1;
        else pre[i]=-2;
    }
    int sum=0;
    For(i,1,n){
        if(pre[i]<0) continue;
        now[++sum]=pre[i];
    }
    sort(now+1,now+sum+1);
    build(1,1,sum);
    For(i,1,n){
        if(pre[i]>=0){
            int tmp=lower_bound(now+1,now+sum+1,pre[i])-now;
            updata(1,1,sum,tmp,1);
            q.push(pre[i]);
        }
        else if(pre[i]==-1){
            int ret=q.front();
            int tmp=lower_bound(now+1,now+sum+1,ret)-now;
            updata(1,1,sum,tmp,-1);
            q.pop();
        }
        else{
            int zz=query(1,1,sum,q.size()/2+1);
            printf("%d\n",now[zz]);
        }
    }
}

int main(){
    while(~scanf("%d",&n)) work();
    return 0;
}

若想更直观的了解权值线段树,看这篇(权值线段树求逆序对)”:
http://blog.csdn.net/Monkey_king2017cn/article/details/60323601

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
哈夫曼树是一种带有权值的二叉树,它的构造方法是通过贪心策略,将权值最小的两个节点合并成一个新节点,直到最后只剩下一个节点为止。下面是给定权值构造哈夫曼树的详细步骤: 1. 将所有权值作为叶子节点,构造一个节点集合。 2. 从节点集合中选出两个权值最小的节点,合并成一个新节点,权值为两个节点的权值之和。 3. 将新节点加入节点集合中,并删除原来的两个节点。 4. 重复步骤2和3,直到节点集合中只剩下一个节点为止,这个节点就是哈夫曼树的根节点。 下面是一个示例,假设给定的权值为[5, 9, 12, 13, 16, 45]: 首先构造出6个叶子节点: ``` 5 9 12 13 16 45 / / / / / / A B C D E F ``` 然后按照上面的步骤,合并权值最小的两个节点: ``` 5 9 12 13 16 45 / / / / / A B C D ┌────F │ 61 │ └────E ``` 继续合并权值最小的两个节点: ``` 5 9 12 13 16 45 / / / / / / A B C ┌────D E F │ 25 │ └─────┐ │ 36 │ └────C ``` 继续合并权值最小的两个节点: ``` 5 9 12 13 16 45 / / / / / / A B ┌─────┐ D E F │ │ 25 36 │ │ └─────┘ │ 61 │ └────C ``` 最后只剩下一个节点,它就是哈夫曼树的根节点: ``` 111 / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ 5 106 / \ / \ / \ / \ / \ / \ A B C 61 / \ / \ D E ``` 因此,给定权值构造出的哈夫曼树就是上面的树形结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值