CF613B 技能
1 CF613B 技能
2 题目描述
时间限制 \(2s\) | 空间限制 \(256M\)
\(Lesha\) 在玩最近发布的传奇游戏 \(hacknet\) 的新版本。在本版本引入了个性技能机制。现在每个玩家的个性有准确的 \(n\) 个技能,每个技能都用非负整数 \(a_i\) 表示这个技能的当前级别。所有技能的最高等级都是 \(A\)。和新增的技能一起还增加了玩家的全局排名。玩家根据一个叫做原力的大小来排名,原力就是一个玩家下面值的累加和。
- 所有满级技能的个数乘以一个系数 \(c_f\)。
- 所有技能中的最小技能等级乘以一个系数 \(c_m\)。
现在 \(Lesha\) 准备花掉 \(m\) 个 \(hacknet\) 货币,每个货币可以给任意一个当前技能等级加一(如果这个技能等级小于 \(A\) )。帮他找出能达到原力的最大值。
数据范围:\(1 ≤ n ≤ 100000, 1 ≤ A ≤ 10^9, 0 ≤ c_f, c_m ≤ 1000, 0 ≤ m ≤ 10^{15}\)
3 题解
我们分析题目,发现:最终的答案与技能本身的顺序无关,只与它们的具体值有关。因此,我们可以先对所有技能按照初始等级排序。
这道题要我们求的是某两个值的和的最大值。虽然这道题看起来可以用 \(dp\) 做,但是状态比较难设计,总体难度较大。我们不如换一个思路:由于答案是由两部分组成的,所以对于当前答案,我们可以将第一部分固定(枚举使得当前情况第一部分值为定值),找到符合条件的最大的第二部分,并与最大值之间取 \(max\)。接下来我们具体阐释如何计算。
第一层循环,我们利用 \(O(n)\) 的时间复杂度枚举我们将几个技能拉满。接下来,我们先计算出将这几个技能拉满后我们还能剩下多少个货币可以升级。如果这里剩不下货币或者剩下的货币数为负,说明我们无法将这几个技能拉满,后面由于拉满的技能数越来越多,肯定无法支付,\(break\) 掉即可。如果可以支付,那么我们考虑二分枚举技能的最小等级。对于当前这个等级,我们可以再利用一个二分,找出最后一个初始等级小于我们第一个二分所求出的等级的技能。然后判断将所有小于等级的技能升到该等级所需要的货币是否多于我们剩余的货币,如果无法支付,那么我们就判断该等级无法达到,否则就可以达到。我们找到最高的可以达到的等级,并将这个等级乘以 \(cm\) 加上第一层循环枚举的满级技能的个数乘以 \(cf\),与 \(ans\) 取 \(max\)。如果当前这种方案中的这个数比 \(ans\) 大,那么我们就记录下拉满的技能的个数和技能的最小等级。这时我们就可以将后面几个初始等级最大的技能全部升满级,然后将所有初始等级小于最小等级的技能拉到最小等级。注意:由于我们一开始按初始等级排序了,这时我们输出的技能序列不是原技能的序列,所以我们需要将整个技能序列按照原技能序列顺序排序。
4 代码(空格警告):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
#define int long long
int n, A, cf, cm, m, Left, ans, alevel, ans1, ans2, aA;
int sum1[N], sum2[N];
struct num
{
int level, id;
}a[N];
bool cmp1(num x, num y)
{
return x.level < y.level;
}
bool cmp2(num x, num y)
{
return x.id < y.id;
}
bool check(int x, int ed)
{
int l = 1, r = ed, mid;
while (l <= r)
{
mid = (l+r)/2;
if (a[mid].level < x) ans2 = mid, l = mid+1;
else r = mid-1;
}
if (Left - ans2 * x + sum1[ans2] < 0) return 0;
return 1;
}
signed main()
{
cin >> n >> A >> cf >> cm >> m;
for (int i = 1; i <= n; i++) cin >> a[i].level, a[i].id = i;
sort(a+1, a+n+1, cmp1);
ans = -1;
for (int i = 1; i <= n; i++)
{
sum1[i] = sum1[i-1] + a[i].level;
sum2[i] = sum2[i-1] + a[n-i+1].level;
}
for (int i = 0; i <= n; i++)
{
int l = a[1].level, r = A - 1, mid;
Left = m - i * A + sum2[i];
if (Left < 0) break;
if (i == n)
{
if (ans < n * cf + A * cm)
{
ans = n * cf + A * cm;
alevel = A;
aA = n;
}
continue;
}
while (l <= r)
{
mid = (l+r)/2;
if (check(mid, n - i)) ans1 = mid, l = mid + 1;
else r = mid - 1;
}
if (ans < i * cf + ans1 * cm)
{
ans = i * cf + ans1 * cm;
alevel = ans1;
aA = i;
}
}
cout << ans << '\n';
for (int i = 1; i <= aA; i++) a[n-i+1].level = A;
for (int i = 1; i <= n; i++)
{
if (a[i].level >= alevel) break;
a[i].level = alevel;
}
sort(a+1, a+n+1, cmp2);
for (int i = 1; i <= n; i++) cout << a[i].level << " ";
return 0;
}