题目链接: Skills CodeForces - 614D
大意
有n项技能, 每项技能的等级为
ai
, 技能最大等级为A,
能力值=等级为A的技能数量⋅cf+minaai⋅cm
现在有m个技能点(一个技能点可以将一个技能升一级, 达到A后就不能再升了), 求能达到的最大能力值
思路
二分
想要提升能力值, 有两种手段, 增加等级为A的技能数量, 让最小值增大
如果我们枚举等级为A的技能的数量(使用技能点提升后), 还剩下x技能点, 接下来就要用这x个技能点使最小值最大化, 二分
先将技能按等级从小到大排序, 然后求前缀和
那么等级为A的技能数量变成i, 所消耗的技能点=i*A - (sum[n]-sum[n-i])
那么剩下的技能点x = m- 已经消耗的
然后在[0, A]范围内二分, 求出最大最小值
二分中check方法是再用一个二分(这里直接用lower_bound)求出技能等级小于mid的数量, 然后和上面计算一样利用sum计算出需要技能点
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+100;
ll n, A, cf, cm, m, sum[maxn];
struct P
{
ll a, id;
P(ll x, ll y) :a(x), id(y){};
P(){}
bool operator<(const P &x) {return a < x.a;}
}p[maxn];
int main()
{
cin >> n >> A >> cf >> cm >> m;
for(int i=0; i<n; ++i)
{
scanf("%I64d", &p[i].a);
p[i].id = i;
}
sort(p, p+n);
for(int i=0; i<n; ++i) sum[i+1] = sum[i]+p[i].a;
int tcnt = 0;
for(int i=0; i<n; ++i) if(p[i].a == A) ++tcnt;
ll ans = tcnt*cf + p[0].a*cm;
int cntm = 0;
int mi = 0;
for(int i=0; i<=n; ++i)
{
ll x = m - (A*i-(sum[n]-sum[n-i]));
if(x<=0) break;
ll l=0, r=A, mid;
while(l<r)
{
mid = (l+r)/2;
int cnt = lower_bound(p, p+n-i, P(mid, 0ll)) - p;
if(cnt*mid-sum[cnt] <= x) l = mid+1;
else r = mid;
}
int tcnt = lower_bound(p, p+n-i, P(l, 0ll))-p;
if(tcnt*l-sum[tcnt] > x) --l;
ll tans = i*cf + l*cm;
if(tans > ans)
{
ans = tans;
cntm = i;
mi = l;
}
}
for(int i=n-1; i>=0 && cntm; --i, --cntm) p[i].a = A;
for(int i=0; i<n; ++i) if(p[i].a < mi) p[i].a = mi;
sort(p, p+n, [](const P&x, P&y) {return x.id < y.id; });
cout << ans << endl;
for(int i=0; i<n; ++i) printf("%I64d ", p[i].a);
cout <<endl;
return 0;
}