给出一个数列,你每次可以讲其中任意一个数加一,但是你最多只可以操作m次,给出一个最大值A,每个数最多能加到A
然后所求的值是(最大值A的个数suma*cf加上整个数列最小值minf*cm)的最大值
cf的题基本上都是脑筋急转弯,首先我们可以枚举操作完后的最大值的个数,然后我们就会发现,如果你要一个值达到最大值的话,一定是从大到小来做,
假设 aj<ak<a
如果你将aj操作为最大值,就相当用ak到了最大值然后让aj变为ak,但是其实让aj变为ak并没什么意义ak-aj这段差距可以用来填最小值的坑,当然如果aj是最小值就去填aj了
所以我们排个序,然后从后往前枚举,从i+1->n都是A,然后从1->i使得最小值尽可能大。
至于最小值最大我用的是二分套二分
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
long long n,A,cf,cm,m;
struct rec
{
long long sum,h,wei,rank;
}skill[100100];
bool check(long long a,long long b,long long x,long long w)
{
long long l=a,r=b,mid;
while (l!=r)
{
mid=((l+r)>>1)+1;
if (skill[mid].h>x) r=mid-1;
else l=mid;
}
if ((l*x-skill[l].sum)<=w) return true;
else return false;
}
long long work(int a,int b,long long w)
{
long long l=skill[a].h,r=A,mid;
while (l!=r)
{
mid=((l+r)>>1)+1;
if (check(a,b,mid,w)) l=mid;
else r=mid-1;
}
return l;
}
int cmp(rec a,rec b) {return a.h<b.h;}
int cmp2(rec a,rec b) {return a.wei<b.wei;}
int main()
{
scanf("%I64d%I64d%I64d%I64d%I64d",&n,&A,&cf,&cm,&m);
long long tot_m=m;
for (int i=1;i<=n;i++)
{
scanf("%I64d",&skill[i].h);
skill[i].wei=i;
}
sort(skill+1,skill+n+1,cmp);
for (int i=1;i<=n;i++)
skill[i].rank=i;
for (int i=1;i<=n;i++)
skill[i].sum=skill[i-1].sum+skill[i].h;
if (n*A<=m+skill[n].sum)
{
printf("%I64d\n",n*cf+A*cm);
for (int i=1;i<n;i++)
printf("%I64d ",A);
printf("%I64d",A);
return 0;
}
long long maxf=-1,ansl,ansr;
for (int i=n;i;i--)
{
long long tmp=work(1,i,m);
if (maxf<tmp*cm+cf*(n-i))
{
maxf=tmp*cm+cf*(n-i);
ansl=tmp;
ansr=i+1;
}
if (m+skill[i].h>=A) m-=(A-skill[i].h);
else break;
}
printf("%I64d\n",maxf);
sort(skill+1,skill+n+1,cmp2);
for (int i=1;i<=n;i++)
{
if (skill[i].rank>=ansr)
{
tot_m-=(A-skill[i].h);
skill[i].h=A;
continue;
}
if (skill[i].h<ansl)
{
tot_m-=(ansl-skill[i].h);
skill[i].h=ansl;
}
}
for (int i=1;i<n;i++)
printf("%I64d ",skill[i].h);
printf("%I64d",skill[n].h);
return 0;
}