集合操作 牛客练习赛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;
}