uoj280 【UTR #2】题目难度提升 堆维护中位数+set
题目传送门
题解
这道题很妙啊。
这种题目如果给予选手足够的时间,每一个选手应该都能做出来。
大概就是核心思路看上去很简单,但是想要推出来并不简单。
首先考虑如果没有重复的元素应该怎么做。
第一个数应该就是最小值。
在没有重复元素的时候,新加入一个数想要保证中位数不下降就必须要满足这个数大于等于前面的中位数。
所以选择加入某个数的时候的判断条件就是加入这个数以后,剩下的最小的数比中位数大。
具体实现的时候,可以先取出这个最小的数。如果前面的元素数量是奇数,那么加入它以后中位数是中间两个数的平均值,所以需要讨论这个最小的数和之前中位数的关系。如果大于之前的中位数,那么我们可以直接选择剩下的数里面最大的——因为不管加进去谁,以后的中位数都没有这个最小的数大,所以可以直接放心地加入最大的;否则,假设之前的中位数为 \(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的更多相关文章
- 【uoj#280】[UTR #2]题目难度提升 对顶堆+STL-set
题目描述 给出 $n$ 个数 $a_1,a_2,...,a_n$ ,将其排为序列 $\{p_i\}$ ,满足 $\{前\ i\ 个数的中位数\}$ 单调不降.求字典序最大的 $\{p_i\}$ . 其 ...
- 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升
[UOJ#278][UTR #2]题目排列顺序 试题描述 “又要出题了.” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这 ...
- 洛谷 P3644 [APIO2015]八邻旁之桥(对顶堆维护中位数)
题面传送门 题意: 一条河将大地分为 \(A,B\) 两个部分.两部分均可视为一根数轴. 有 \(n\) 名工人,第 \(i\) 名的家在 \(x_i\) 区域的 \(a_i\) 位置,公司在 \(y ...
- uoj#280. 【UTR #2】题目难度提升(构造)
传送门 咱先膜一下\(GXZ\)再说 我们先把序列从小到大排序,然后分情况讨论 无解是不存在的,从小到大输出所有数肯定可行 情况一,如果\(a[mid]=a[mid+1]\),因为最终的中位数也是它们 ...
- 【UOJ #280】【UTR #2】题目难度提升
http://uoj.ac/problem/280 非常难想的贪心,用set\(O(nlogn)\). 调了一天qwq. 题解 #include<set> #include<cstd ...
- 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp
题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...
- bzoj4165 矩阵 堆维护多路归并
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4165 题解 大概多路归并是最很重要的知识点了吧,近几年考察也挺多的(虽然都是作为签到题的). ...
- [UOJ#268]. 【清华集训2016】数据交互[动态dp+可删堆维护最长链]
题意 给出 \(n\) 个点的树,每个时刻可能出现一条路径 \(A_i\) 或者之前出现的某条路径 \(A_i\) 消失,每条路径有一个权值,求出在每个时刻过后能够找到的权值最大的路径(指所有和该路径 ...
- POJ 2010 Moo University - Financial Aid(堆维护滑窗kth,二分)
按照score排序,贪心,从左到右用堆维护并且记录前面的最小N/2个花费之和. 然后从右向左枚举中位数,维护N/2个数之和加上并判断是否满足条件.(stl的队列没有clear(),只能一个一个pop. ...
随机推荐
- 方法一破解:Excel工作表保护密码
在excel2016中实测验证过有效 在Excel中,为了保护自已的工作表不被修改,我们可以添加保护密码. 操作步骤: 1.把Excel文件的扩展名xlsx修改为Rar.瞬间Excel文件变成了压缩包 ...
- python - DBUtils 连接池减少oracle数据库的连接数
问题: 接到需求,告知项目的oracle连接次数过多,对系统造成太过大的负担,要求减少oracle数据库的连接次数 分析: 仔细分析代码以后,发现产生问题的原因,在于之前要求提升oracle监控的监控 ...
- Stream介绍
一.Stream介绍 现在有这样的需求:有个菜单list,菜单里面非常多的食物列表,只选取小于400卡路里的并且按照卡路里排序,然后只想知道对应的食物名字. 代码: package com.cy.ja ...
- Octavia 的 HTTPS 与自建、签发 CA 证书
目录 文章目录 目录 Octavia 为什么需要自建 CA 证书? GenerateServerPEMTask CertComputeCreate Amphora Agent AmphoraAPICl ...
- Tomcat中出现"RFC 7230 and RFC 3986"错误的解决方法
在用axios从前台向后台发请求时,后台报错 Invalid character found in the request target. The valid characters are defin ...
- 超详细 SpringMVC @RequestMapping 注解使用技巧
@RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一.这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上. 在这篇文章中,你将会看到 @R ...
- mysql常用知识点之limit
limit函数的应用.limit后面跟整数,如limit 5,表示在结果集中取前5条:limit后跟整数区间,如limit 2,5,表示在结果集中 从第3条开始,取5条数据,第一个整数表示结果集的顺序 ...
- 手把手教你SOAP访问webservice并DOM解析返回的XML数据(转)
http://blog.csdn.net/u012534831/article/details/51357111 前言: 目前我们项目组还在采用webservice这种http方式,并且某些网站服务提 ...
- Miller-Robin 素数测试法 模板
测试单个素数,出错概率比计算机本身出错的概率还要低 算法是基于费马小定理(format),二次探测定理(x*x % p == 1 ,若P为素数,则x的解只能是x = 1或者x = p - 1)加上迭代 ...
- [转帖]同事推荐的的aira2
Windows系统安装最新版Aria2客户端及使用教程 https://www.moerats.com/archives/519/ 改天学习一下. 说明:之前都是说的在Linux VPS服务器上安装A ...