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

时间:2020-04-11
本文章向大家介绍#280. 【UTR #2】题目难度提升,主要包括#280. 【UTR #2】题目难度提升使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目描述

最终吉米多出题斯基没有找到优质的题目,于是高产的吉米多出题斯基决定自己出一道。

首先,吉米多出题斯基脑洞了一道难度为 h1 的题目,然而吉米多出题斯基觉得太水了,于是把这题稍微改了改变成了一道难度为 h2 的。然而吉米多出题斯基还是觉得太水了,又稍微改了改变成了难度为 h3 的。如此进行下去吉米多出题斯基脑洞出了 n 个版本的题目,难度分别为 h1,…,hn。

由于吉米多出题斯基在脑洞的时候只是随便改了改题目条件,所以每次改题并不一定会变难。但神奇的是,每次产生一个新版本的题目后,吉米多出题斯基手上所有版本的题目难度的中位数不会降低。

很快吉米多出题斯基出好了题,造好了 UOI,选手们纷纷阵亡。多年后吉米多出题斯基再看到自己曾经出的这道题时感叹道:“都是回忆啊……”

可是吉米多出题斯基突然发现自己只记得脑洞过程产生的 n 个版本难度是 a1,…,an,却不记得每个 ai 对应的是第几个版本了。

吉米多出题斯基日理万机没有时间再细想了,于是他找到了你 —— 风璃殇做不出题耶维奇,请你帮助吉米多出题斯基把 a1,…,an 排列顺序,给出一组满足条件且字典序最大的 h1,…,hn 吧!

对于一个数列 v1,…,vm,若 m 为奇数则定义中位数为从小到大第 ⌈m/2⌉ 的数;若 m 为偶数则定义中位数为从小到大第 m/2 和第 m/2+1 的数的平均值。

输入格式

第一行一个正整数 n。

接下来一行 n 个整数 a1,…,an。

输出格式

一行,n 个整数表示你找到的字典序最大的 h1,…,hn。

如果无解,输出卖萌表情 "QwQ"。

样例一

input

5
1 2 3 4 5

output

1 3 2 5 4

explanation

中位数依次为:{1,2,2,2.5,3}。

样例二

input

8
1 2 2 3 3 3 4 4

output

3 3 4 3 4 2 2 1

样例三

见样例数据下载。

限制与约定

子任务 分值 n 其他约定
1 10 1≤n≤10 无
2 10 1≤n≤100 无
3 20 1≤n≤2000 无
4 30 1≤n≤105 ai 互不相同
5 30 1≤n≤105 无
对于所有数据,满足 1≤ai≤109。

时间限制:1s
空间限制:256MB

对顶堆

这是一个大型分类讨论现场。。。
讲a 从小到大排序

a[mid] = a[mid+1]
那么左一个右一个直接选完,即可得到最优解。

否则 往左扫,直到扫到有挨着相同的。
也是左一个右一个,但是肯定有剩余。
把已经选了的放入(对顶)堆里,维护中位数
没选的放入multiset中。
分类讨论即可。。。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<bitset>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int n;
int a[N] , vis[N];
priority_queue<int> A , B;
multiset<int> s;

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() - 1 > B.size()) B.push(-A.top()) , A.pop();
	return ;
}

void solve()
{
	sort(a + 1 , a + 1 + n); int mid = (n + 1) >> 1;
	if(a[mid] == a[mid + 1])
	{
		while(mid < n && a[mid] == a[mid + 1]) mid++;
		cout << a[mid] << ' '; int p = mid - 1 , q = n;
		while(p || q > mid)
		{
			if(p) cout << a[p--] << ' ';
			if(q > mid) cout << a[q--] << ' ';
		}
		cout << '\n'; return ;
	}
	while(mid > 1 && a[mid] != a[mid - 1]) mid--;
	vis[mid] = 1; cout << a[mid] << ' '; int p = mid - 1 , q = n;
	while(p && q > mid) cout << a[p] << ' ' , vis[p--] = 1 , cout << a[q] << ' ' , vis[q--] = 1;
	for(int i = 1 ; i <= n ; ++i) if(!vis[i]) s.insert(a[i]); else push(a[i]);
	while(!s.empty()) // 满足堆里的中位数 <= 没选的的最小值 
	{
		int p = *s.begin() , q;
		if(A.size() == B.size())
		{
			if(p >= -B.top()) q = *--s.end();  // 最小值比中位数大。怎么选也合法,选最大的字典序更大 
			else q = *s.begin(); // 再不选那个*s.begin就 < 堆中的最小值。 
		}
		else
		{
            if(!B.empty() && p * 2 >= A.top() - B.top()) q = *--s.end(); // 同上 
            else q = *--s.upper_bound(p * 2 - A.top()); // 偶数时不一定选他自己,可以选中间两个数的中间的数,比如2 , 10 , 可以不选3,选6, 3可以之后再选 
		}
		cout << q << ' '; s.erase(s.find(q)); push(q); // s.erase(find(q)) 是只删一个 
	}
	return ;
}

int main()
{
	freopen("c.in" , "r" , stdin);
	freopen("c.out" , "w" , stdout);
	n = read();
	for(int i = 1 ; i <= n ; ++i) a[i] = read();
	solve();
	fclose(stdin); fclose(stdout);
	return 0;
}
/*
10
1 2 2 3 3 3 4 4 5 6
*/

原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12681113.html