题目描述

给出 $n$ 个数 $a_1,a_2,...,a_n$ ,将其排为序列 $\{p_i\}$ ,满足 $\{前\ i\ 个数的中位数\}$ 单调不降。求字典序最大的 $\{p_i\}$ 。

其中,对于一个长度为 $m$ 的数列,若 $m$ 为奇数,则中位数为从小到大第 $\lceil\frac m2\rceil$ 大的数;若 $m$ 为偶数,则中位数为从小到大第 $\frac m2$ 大和第 $\frac m2+1$ 大的数的平均值。


题解

对顶堆+STL-set

显然如果已经知道了这个数列的一部分,剩下的一定是每次加入大于等于中位数的数。

那么如何确定这一“部分呢”?将 $a$ 从小到大排序,然后:

  • 如果 $a_{\lceil\frac n2\rceil}=a_{\lceil\frac n2\rceil+1}$ ,则可以让任何时刻中位数都等于 $a_{\lceil\frac n2\rceil}$ ,找到最大的 $k$ 使得 $a_k+1=a_{\lceil\frac n2\rceil}$ ,按照 $k,k+1,k-1,k+2,k-2,...$ 的顺序选择完整个数列即可得到最优解,显然任何时刻中位数都相等。没有考虑到这种情况可以得到60分。
  • 否则如果存在 $k<\lceil\frac n2\rceil$ 且 $a_k=a_{k+1}$ ,则按照 $k,k+1,k-1,k+2,k-2,...$ 的顺序选择,直到前面没有数可以取,这个过程中位数都相等。没有考虑到这种情况只能得到所有数互不相同的40分。
  • 否则选择第一个数。

然后使用multiset保证每次删除后最小的数大于等于中位数,使用对顶堆维护中位数即可。

对顶堆:使用大根堆维护较小数,使用小根堆维护大数,保证两个堆的大小差不超过1。显然中位数可以直接从两个堆的堆顶元素得到。

时间复杂度 $O(n\log n)$ 。

#include <set>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
multiset<int> s;
priority_queue<int> A , B;
int a[N] , v[N];
inline void push(int x)
{
if(A.empty() || x <= A.top()) A.push(x);
else B.push(-x);
if(A.size() < B.size()) A.push(-B.top()) , B.pop();
if(A.size() - B.size() > 1) B.push(-A.top()) , A.pop();
}
int main()
{
int n , i , p , q , mid;
scanf("%d" , &n);
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]);
sort(a + 1 , a + n + 1) , mid = (n + 1) >> 1;
if(a[mid] == a[mid + 1])
{
while(mid < n && a[mid] == a[mid + 1]) mid ++ ;
printf("%d" , a[mid]) , p = mid - 1 , q = n;
while(p || q > mid)
{
if(p) printf(" %d" , a[p -- ]);
if(q > mid) printf(" %d" , a[q -- ]);
}
return 0;
}
while(mid > 1 && a[mid] != a[mid - 1]) mid -- ;
printf("%d" , a[mid]) , v[mid] = 1 , p = mid - 1 , q = n;
while(p && q > mid) printf(" %d" , a[p]) , v[p -- ] = 1 , printf(" %d" , a[q]) , v[q -- ] = 1;
for(i = 1 ; i <= n ; i ++ )
{
if(v[i]) push(a[i]);
else s.insert(a[i]);
}
while(!s.empty())
{
p = *s.begin();
if(A.size() == B.size())
{
if(p >= -B.top()) q = *--s.end();
else q = *s.begin();
}
else
{
if(!B.empty() && p * 2 >= A.top() - B.top()) q = *--s.end();
else q = *--s.upper_bound(p * 2 - A.top());
}
printf(" %d" , q) , s.erase(s.find(q)) , push(q);
}
return 0;
}

【uoj#280】[UTR #2]题目难度提升 对顶堆+STL-set的更多相关文章

  1. 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

    [UOJ#278][UTR #2]题目排列顺序 试题描述 “又要出题了.” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这 ...

  2. uoj#280. 【UTR #2】题目难度提升(构造)

    传送门 咱先膜一下\(GXZ\)再说 我们先把序列从小到大排序,然后分情况讨论 无解是不存在的,从小到大输出所有数肯定可行 情况一,如果\(a[mid]=a[mid+1]\),因为最终的中位数也是它们 ...

  3. 【UOJ #280】【UTR #2】题目难度提升

    http://uoj.ac/problem/280 非常难想的贪心,用set\(O(nlogn)\). 调了一天qwq. 题解 #include<set> #include<cstd ...

  4. uoj280 【UTR #2】题目难度提升 堆维护中位数+set

    题目传送门 http://uoj.ac/problem/280 题解 这道题很妙啊. 这种题目如果给予选手足够的时间,每一个选手应该都能做出来. 大概就是核心思路看上去很简单,但是想要推出来并不简单. ...

  5. 洛谷 - P1801 - 黑匣子 - 对顶堆

    这道题是提高+省选-的难度,做出来的话对数据结构题目的理解会增加很多. 可以使用一种叫做对顶堆的东西,对顶堆是在线维护第n小的logn的算法.大概的思路是,假如我们要找的是第n小,我们就维护一个大小为 ...

  6. 【Luogu P1168】【Luogu P1801&UVA 501】中位数&黑匣子(Black Box)——对顶堆相关

    Luogu P1168 Luogu P1801 UVA 501(洛谷Remote Judge) 前置知识:堆.优先队列STL的使用 对顶堆 是一种在线维护第\(k\)小的算法. 其实就是开两个堆,一个 ...

  7. poj3784(对顶堆)

    题意:多组数据,让你求出1~i(i为奇数&&i<=n)的中位数 思路:首先复杂度必为O(n)或O(nlogn)的(数据范围) 思索,如果题目要求1次中位数,好求!排个序,取a[( ...

  8. hdu3282 链表或者对顶堆

    维护序列的动态中位数 第一次用链表做题..感觉指针指来指去也挺麻烦的.. 本题链表解法就是用数组模拟出一个链表,然后离线输入所有数,排序,按照输入顺序在链表里删除元素,一次性删掉两个,然后中位数指针对 ...

  9. hdu4261 Estimation[暴力dp+对顶堆]

    https://vjudge.net/problem/HDU-4261 对于一个长2000的数列划分最多25个块,每块代价为块内每个数与块内中位数差的绝对值之和,求最小总代价. 套路化地,设$f[i] ...

随机推荐

  1. python将oracle中的数据导入到mysql中。

    一.导入表结构.使用工具:navicate premium 和PowerDesinger 1. 先用navicate premium把oracle中的数据库导出为oracle脚本. 2. 在Power ...

  2. 底部线条css样式

    1.首先固定宽高 (将文字移至左边,例如 “姓名:”) .line{ width:100%; height:40px; float:left; border-bottom:1px solid #ccc ...

  3. JMeter下Groovy和BeanShell语言在不同组件中性能差异实践探究

    一般而言JMeter下性能最好的是jar包这类java原生请求,对于JMeter并没有原生支持的请求,一般都会将其直接编译为jar包,然后再JMeter中调用,这样性能最好. 但是有些需求并不适合用j ...

  4. Float浮点数的使用和条件

    在这里简单的说一下,我对浮点数的理解,可能说的比较浅,老师也没有说,只是略微的提了一下,完全是我自己个人的理解. 我觉得float浮点数的用法和int的用法有些雷同,浮点数用于计算小数点单位,我们先可 ...

  5. C++ 单例模式总结与剖析

    目录 C++ 单例模式总结与剖析 一.什么是单例 二.C++单例的实现 2.1 基础要点 2.2 C++ 实现单例的几种方式 2.3 单例的模板 三.何时应该使用或者不使用单例 反对单例的理由 参考文 ...

  6. socket 编程 : shutdown vs close

    TCP/IP 四次挥手 首先作者先描述一下TCP/IP 协议中四次挥手的过程,如果对此已经熟悉的读者可以跳过本节. 四次挥手 这是一个很经典的示例图,众所周知tcp socket 在一个生命周期中有很 ...

  7. 【神经网络】自编码聚类算法--DEC (Deep Embedded Clustering)

    1.算法描述 最近在做AutoEncoder的一些探索,看到2016年的一篇论文,虽然不是最新的,但是思路和方法值得学习.论文原文链接 http://proceedings.mlr.press/v48 ...

  8. 满帮集团CEO:未来将向“智慧型”公司转变,要成为一家生态公司

    谁都想成为下一个滴滴.显然,王刚也希望在物流业,货车帮与运满满在合并后,能够企及滴滴的高度. 货车帮与运满满,都曾是货运物流领域的翘楚,也因为业务的竞争关系有过水火不容厮杀.但最终还是在资本与地方政府 ...

  9. [leetcode-897-Increasing Order Search Tree]

    Given a tree, rearrange the tree in in-order so that the leftmost node in the tree is now the root o ...

  10. Dao DaoImp

    DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后在Spring的配置文件中定义此接口的实现类,然后就可在模块中调用此接口 ...