题目传送门

http://uoj.ac/problem/280

题解

这道题很妙啊。

这种题目如果给予选手足够的时间,每一个选手应该都能做出来。

大概就是核心思路看上去很简单,但是想要推出来并不简单。


首先考虑如果没有重复的元素应该怎么做。

第一个数应该就是最小值。

在没有重复元素的时候,新加入一个数想要保证中位数不下降就必须要满足这个数大于等于前面的中位数。

所以选择加入某个数的时候的判断条件就是加入这个数以后,剩下的最小的数比中位数大。

具体实现的时候,可以先取出这个最小的数。如果前面的元素数量是奇数,那么加入它以后中位数是中间两个数的平均值,所以需要讨论这个最小的数和之前中位数的关系。如果大于之前的中位数,那么我们可以直接选择剩下的数里面最大的——因为不管加进去谁,以后的中位数都没有这个最小的数大,所以可以直接放心地加入最大的;否则,假设之前的中位数为 \(pp\),新加入的数为 \(x\),之前取出的最小数为 \(v\),那么之后的中位数 \(\frac{pp + x}2\) 要 \(\geq v\)。所以 \(x\) 可以取 \(\leq 2v - pp\) 的最大的数。

如果之前元素数量是偶数,那么加入以后它的中位数就是一个固定的数,不随加入了哪个数改变。如果最小的数大于等于这个数,那么直接加入最大的都不会有影响;否则加入最小的数。

维护中位数可以使用堆。


考虑有了重复的元素的做法。

如果重复的元素在整个集合的 \(mid\) 的后面,那么不会影响。

如果经过 \(mid\) 那么可以直接以经过 \(mid\) 的这个重复元素的值的最后一位为开始,先输出这个数,然后输出小于等于这个数的最大数,然后大于这个数的最大的数,并删除;重复这个过程,知道后面被删光了。然后把剩下的从大到小输出就可以了。正确性很显然。

如果不经过呢,那么我们找到小于 \(mid\) 的最大的重复元素,先把小于这个重复元素的全部按照一个小于等于这个数的最大数,一个大于这个数的最大的数的方案输出完。下面应该只剩下大于这个重复元素的了,可以按照上面没有重复元素的做法。


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

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
} const int N = 1e5 + 7; int n, pp, sz;
int a[N];
std::multiset<int> s;
std::priority_queue<int, std::vector<int>, std::greater<int> > q; inline void qadd(int x) {
++sz, q.push(x);
while (q.size() * 2 > sz) pp = q.top(), q.pop();
} inline void work() {
std::sort(a + 1, a + n + 1);
if (a[(1 + n) / 2] == a[(1 + n) / 2 + 1]) {
int p = (1 + n) / 2, p1, p2 = n;
while (p < n && a[p] == a[p + 1]) ++p;
p1 = p;
printf("%d ", a[p1--]);
// dbg("******** %d %d\n", p1, p2);
while (p1 && p2 > p) printf("%d %d ", a[p1--], a[p2--]);//, dbg("*** %d %d, -> %d\n", p1, p2, (1 + n) / 2);
while (p1) printf("%d ", a[p1--]);
return;
}
// dbg("****************\n");
int p, p1, p2;
for (p = (1 + n) / 2; p > 1 && a[p] != a[p - 1]; --p) ;
if (p > 1 && a[p] == a[p - 1]) {
p1 = p, p2 = n;
printf("%d ", a[p1--]);
while (p1 && p2 > p) printf("%d %d ", a[p1--], a[p2--]);
for (int i = 1; i <= p1; ++i) s.insert(a[i]);
for (int i = p1 + 1; i <= p; ++i) qadd(a[i]);
for (int i = p + 1; i <= p2; ++i) s.insert(a[i]);
for (int i = p2 + 1; i <= n; ++i) qadd(a[i]);
} else {
qadd(a[1]);
printf("%d ", a[1]);
for (int i = 2; i <= n; ++i) s.insert(a[i]);
}
// dbg("****************\n");
while (!s.empty()) {
int min = *s.begin();
std::multiset<int>::iterator it;
if (!(sz & 1)) {
if (min < q.top()) it = s.begin();
else it = s.end(), --it;
} else {
if (q.empty() || min * 2 < pp + q.top()) it = s.upper_bound(min * 2 - pp), --it;
else it = s.end(), --it;
}
qadd(*it), printf("%d ", *it), s.erase(it);
}
} inline void init() {
read(n);
for (int i = 1; i <= n; ++i) read(a[i]);
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}

uoj280 【UTR #2】题目难度提升 堆维护中位数+set的更多相关文章

  1. 【uoj#280】[UTR #2]题目难度提升 对顶堆+STL-set

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

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

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

  3. 洛谷 P3644 [APIO2015]八邻旁之桥(对顶堆维护中位数)

    题面传送门 题意: 一条河将大地分为 \(A,B\) 两个部分.两部分均可视为一根数轴. 有 \(n\) 名工人,第 \(i\) 名的家在 \(x_i\) 区域的 \(a_i\) 位置,公司在 \(y ...

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

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

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

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

  6. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

  7. bzoj4165 矩阵 堆维护多路归并

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4165 题解 大概多路归并是最很重要的知识点了吧,近几年考察也挺多的(虽然都是作为签到题的). ...

  8. [UOJ#268]. 【清华集训2016】数据交互[动态dp+可删堆维护最长链]

    题意 给出 \(n\) 个点的树,每个时刻可能出现一条路径 \(A_i\) 或者之前出现的某条路径 \(A_i\) 消失,每条路径有一个权值,求出在每个时刻过后能够找到的权值最大的路径(指所有和该路径 ...

  9. POJ 2010 Moo University - Financial Aid(堆维护滑窗kth,二分)

    按照score排序,贪心,从左到右用堆维护并且记录前面的最小N/2个花费之和. 然后从右向左枚举中位数,维护N/2个数之和加上并判断是否满足条件.(stl的队列没有clear(),只能一个一个pop. ...

随机推荐

  1. 基于RANSAC的点云面分割算法

    该算法在RANSAC和空间检索树的基础上实现的. 算法思路: 1.点云抽希.法线估计 2.出局点索引存储声明 3.平面检测 for (size_t i = 0; i < cloudTemp-&g ...

  2. python学习笔记:(八)条件语句

    if语句,python中if语句的一般形式如下: conditon1为真,执行statement_block_1 condition1为假,判断conition_2,如果condition_2为真,执 ...

  3. spring几种获取 HttpServletRequest 对象的方式

    以下的 request 实例都编号了,一共 4 种 方式 1.@Autowired 方式2.public void Test(HttpServletRequest request1, HttpServ ...

  4. 彻底理解RSA算法原理

    1. 什么是RSA RSA算法是现今使用最广泛的公钥密码算法,也是号称地球上最安全的加密算法.在了解RSA算法之前,先熟悉下几个术语 根据密钥的使用方法,可以将密码分为对称密码和公钥密码 对称密码:加 ...

  5. HTML5 列表、表格、媒体元素

    无序列表 <ul> <li>范冰冰演藏族女孩</li> <li>拍集体合影后自拍</li> <li>诗隆甜蜜出游</li& ...

  6. C语言第八周编程作业

        这个作业属于哪个课程 C语言程序设计 这个作业要求在哪 https://edu.cnblogs.com/campus/zswxy/computer-scienceclass3-2018/hom ...

  7. 一些重温CSS需要注意的小细节

    <!-- CSS是用于描述页面展示的语言 字体.颜色.大小.间距,将内容分为多列 或者简单的动画及其他的装饰效果 决定了长啥样 html房子的骨架 css负责装修 怎么装修一个房子呢? 首先就是 ...

  8. Java数据结构之栈(Stack)

    1.栈(Stack)的介绍 栈是一个先入后出(FILO:First In Last Out)的有序列表. 栈(Stack)是限制线性表中元素的插入和删除只能在同一端进行的一种特殊线性表. 允许插入和删 ...

  9. 介绍一下 except 的作用和用法?

    except: #捕获所有异常 except: <异常名>: #捕获指定异常 except:<异常名 1, 异常名 2> : 捕获异常 1 或者异常 2 except:< ...

  10. 阿里云服务器重启出现An error occurred 如何处理

    最近网站重启阿里云服务后,出现 An error occurred, An error occurred. Sorry, the page you are looking for is current ...