集合操作 牛客练习赛83

原题链接
考察:贪心+思维
思路:
  将所有数字化为k*p+b,每次优先队列更新最大值,都会将最大值的k压为次大值的k.我们从大到小遍历,求解k次操作后,最大值的k,已经确定最大值所在位置idx.
  求完之后,k可能有多.需要将idx~n 之间的数再均摊k.但是注意它们只能均摊idx~n长度的k.剩下的k,已经压到106内,可以直接模拟.

Code

#include <iostream> 
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
int n;
LL k,p,a[N];
priority_queue<LL,vector<LL>,less<LL> > q;
LL get(LL x)
{
	return x/p;
}
void Myprintf()
{
	for(int i=1;i<n;i++) printf("%lld ",a[i]);
	printf("%lld\n",a[n]);
}
int main()
{
	scanf("%d%lld%lld",&n,&k,&p);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	sort(a+1,a+n+1);
	if(!p||!k) { Myprintf(); return 0;}
	int now = 1;
	for(int i=n;i>1;i--)
	{
		LL d = get(a[i])-get(a[i-1]);
		LL cnt = n-i+1;
		if(k>=d*cnt)
		{
			k-=d*cnt;
			continue;
		}
		now = i;
		break;
	}
	LL s = get(a[now]);
	for(int i=now;i<=n;i++)
	{
		LL d = get(a[i]);
		a[i]-=(d-s)*p;//减去k减少的. 
	}
	int cnt = n-now+1;
	LL t1 = k/cnt;k%=cnt;
	for(int i=now;i<=n;i++) a[i]-=t1*p;
    for(int i=1;i<=n;i++) q.push(a[i]);
	while(k--)
	{
		LL t = q.top();
		q.pop();
		q.push(t-p);
	}
	for(int i=n;i>=1;i--)
	{
		a[i] = q.top();
		q.pop();
	}
	Myprintf();
	return 0;
}
posted @ 2021-05-29 09:15  acmloser  阅读(42)  评论(0编辑  收藏  举报