转载自:http://dirtytao.com/codeforces-round-339-div-2/
题意:
一共n个技能,最大等级都为A,当前等级已给出。现在有m个技能点,需要通过这些技能点使“战斗力”最大化战斗力=满级技能数Cf + 最低技能等级Cm,输出最大化的战斗力和对应的技能等级。
思路:
先将所有技能等级和位置都记录下来,按照等级从小到达进行排序,然后求出前缀和sum[i]。接下来我们能做两种操作:①.把当前技能等级高的提升为为满级。②把 当前技能等级最低的技能提升到一定等级。我们可以枚举满级的技能数i的所有情况,使用技能点为A*i-(sum[n]-sum[i]),记为p_cost。剩下技能点为m-p_cost,记为left_cost。我们用剩下来的left_cost来填充最低技能点:找到技能left_cost所能填充的最高高度就可以了(有种把水倒进阶梯等增的的水池的感觉),如图所示,A区为填充最高技能所需要的技能点,B区为填充最低技能所需要的技能点,A+B=m,最低技能等级为min(A,(sum[j]+left_cost)/j),j表示B区覆盖的技能数。于是我们只要枚举下所有情况,维护一个最大战斗力的值就可以了。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define ll __int64
#define lson l,m,rt*2
#define rson m+1,r,rt*2+1
const int maxn = 1000001;
const int INF=1<<30;
const double EPS = 1e-5;
const double PI = acos(-1.0);
struct node
{
int x;
int id;
}a[100005];
ll sum[100005];
bool cmp1(node a,node b)
{
return a.x<b.x;
}
bool cmp2(node a,node b)
{
return a.id<b.id;
}
int main()
{
int t,n,i,j,cf,cm;
ll A,m;
while(scanf("%d%I64d%d%d%I64d",&n,&A,&cf,&cm,&m)!=EOF)
{
for(i=0;i<n;++i)
{
scanf("%d",&a[i].x);
a[i].id=i;
}
sort(a,a+n,cmp1);
sum[0]=0;
for(i=1;i<=n;++i)
{
sum[i]=sum[i-1]+a[i-1].x;
}
ll mskill;
ll mk;
ll ans=-1;
int l,r;
for(i=0,j=0;i<=n;++i)
{
ll p_cost=(n-i)*A-(sum[n]-sum[i]);//perfect需要消耗的货币
if(p_cost>m) continue;
ll left_cost=m-p_cost;//剩余的货币
for(;j<i;++j)
{
if((ll)(j+1)*a[j].x-sum[j+1]>left_cost)
break;
}
if(j==0)//m的钱足够点满所有技能点
{
mskill=A;
}
else
{
--j;
mskill=min(A,(sum[j+1]+left_cost)/(j+1));//minimum skill level不会超过A的限制
}
if(ans<mskill*cm+(n-i)*cf)
{
ans=mskill*cm+(n-i)*cf;
l=j;
r=i;
mk=mskill;
}
}
printf("%I64d\n",ans);
for(i=0;i<n;++i)
{
if(i<=l)
{
a[i].x=mk;
}
else if(i>=r)
{
a[i].x=A;
}
}
sort(a,a+n,cmp2);
for(i=0;i<n;++i)
{
printf("%d",a[i].x);
if(i!=n-1) printf(" ");
else puts("");
}
}
return 0;
}