uoj280 【UTR #2】题目难度提升

题面

http://uoj.ac/problem/280

题解

好久没有更博客了啊。。
这题,官方题解似乎炸了,403了,网上也没有找到
那就来填一下坑吧
当然,可能写得不是很严谨。。那大家就感受一下?

首先,通过玩样例二,我们可以发现,如果说,我们存在一个中间的数,他出现了两次或以上,那么我们显然就让他一直作为中位数是最优的,然后填数一定是一小一大,一小一大这么填。你会发现,在这种情况下,这个填数方案是最优的。

因为要字典序最大,因此,我们一定希望前面的人最大
先考虑一下没有相同的数怎么做
容易发现这么一个结论,就是如果你当前的还没有填的最小值比中位数要小
那么你一定GG了
因为你无论在什么时候,填入这个最小值,他都会使得中位数变小
那么就不难得到一个做法,就是我们在每一位填之前,寻找一个我们能填的最大的数,且满足填了以后,最小的数还是不比中位数小的是什么,这个用set就能解决了
至于怎么维护中位数,我们可以维护两个堆

然后,我们再来考虑一下有相同的数怎么做?
如果相同的数,出现在中间数以后,也就是坐标在 ( n + 1 ) / 2 (n+1)/2 (n+1)/2以后,那么他肯定是没有用的。因为最后的中位数一定比这些数小,因此,我们不可能存在一个时刻,使得这些数成为中位数,那么也就是他们是不是一样的没有任何影响,只是拿来凑数的,可以直接把他们看做不同的,并不会影响答案。
那么,我们再考虑,如果这些数出现在中间数之前,那么显然地,我们可以让前半段的中位数直接变成他。具体来说,就是我们找一个在最大的一个有重复数字的,然后和之前第一步那个一样。先填两个,然后一小一大,一小一大地填。
然后剩下的数,就是没有重复的了

直接按照没有相同的数来做就OK了

接着这题就做完了,代码也挺好写的

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
using namespace std;
const int N=100005;
int A[N];
int n;
bool ok[N];//这个点用了没
priority_queue<int> a,b;
void Push (int x)
{
	if (a.empty()||x<=a.top()) a.push(x);
	else b.push(-x);
	if (a.size()<b.size())	{int x=b.top();b.pop();a.push(-x);}
	if (a.size()>b.size()+1) {int x=a.top();a.pop();b.push(-x);}
}
multiset<int> s;
multiset<int>::iterator it;
int main()
{
	scanf("%d",&n);
	for (int u=1;u<=n;u++) scanf("%d",&A[u]);
	sort(A+1,A+1+n);int mid=(n+1)>>1;
	if (A[mid]==A[mid+1])
	{
		while (A[mid]==A[mid+1]) mid++;
		printf("%d ",A[mid]);
		int p=n,q=mid-1;
		while (p>mid||q>0)
		{
			if (q>0) printf("%d ",A[q--]);
			if (p>mid) printf("%d ",A[p--]);
		}
		return 0;
	}
	memset(ok,false,sizeof(ok));
	while (mid>1&&A[mid-1]!=A[mid]) mid--;
	ok[mid]=true;
	printf("%d ",A[mid]);
	int p=n,q=mid-1;
	while (p>mid&&q>0)
	{
		ok[q]=true;printf("%d ",A[q--]);
		ok[p]=true;printf("%d ",A[p--]);
	}
	for (int u=1;u<=n;u++)
	{
		if (ok[u]) Push(A[u]);
		else s.insert(A[u]);
	}
	while (!s.empty())
	{
		int x=*s.begin();
		int ans;
		if (a.size()==b.size())//加入这个数以后是变成单个数的  
		{
			if (x>=(-b.top()))	ans=*(--s.end());
			else ans=x;
		}
		else//变成平均数的 
		{
			if (!b.empty()&&(x*2>=a.top()-b.top())) ans=*(--s.end());
			else 
			{
				ans=*(--s.upper_bound(x*2-a.top()));
			}
		}
		printf("%d ",ans);
		Push(ans);s.erase(s.find(ans));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值