技能升级

技能升级

小蓝最近正在玩一款 RPG 游戏。

他的角色一共有 N 个可以加攻击力的技能。

其中第 i 个技能首次升级可以提升 Ai 点攻击力,以后每次升级增加的点数都会减少 Bi

AiBi(上取整)次之后,再升级该技能将不会改变攻击力。

现在小蓝可以总计升级 M 次技能,他可以任意选择升级的技能和次数。

请你计算小蓝最多可以提高多少点攻击力?

输入格式

输入第一行包含两个整数 NM

以下 N 行每行包含两个整数 AiBi

输出格式

输出一行包含一个整数表示答案。

数据范围

对于 40% 的评测用例,1N,M1000
对于 60% 的评测用例,1N1041M107
对于所有评测用例,1N1051M2×1091Ai,Bi106

输入样例:

3 6
10 5
9 2
8 1

输出样例:

47

 

解题思路

  为了得到最大的总和,那么应该取前m最大的数,因此很容易想到多路归并,每次取这n组数中最大的那一个,具体做法是把先把这n个元素压到优先队列(大根堆),每次取堆顶元素进行累加,然后再把这个数减去对应的bi再压入优先队列中(如果变化后小于等于0就不再压入),重复该步骤直到队列为空或进行了m次,这种做法的时间复杂度为O(mlogn),会超时。

  为了求出前m个最大值,现在换一种思路,考虑从大到小数排名为第m位的数值是多少,假设数值为x。可以通过二分数值来找到这个数x,在所有技能点所能构成的数中,可以发现x的数的个数m个(可能有多个x重复),而x+1的数的个数<m个。对于小于等于x的数值t,可以发现t的数的个数m个;对于大于x的数值t,可以发现t的数的个数<m个。因此具有二段性,可以进行二分。当二分到mid,在check的时候只需要遍历所有数,看一下以ai为首项,bi为公差的等差数列中有多少项时大于等于mid的,如果aimid,那么就有aimidbi+1项。因此时间复杂度为O(nlog106)

  求出来第m个数x后,接下来就要求前m个数的总和,也就是把所有大于等于x的数求和。方法是遍历一遍所有数,如果有aix,然后求t=aimidbi+1,那么根据等差数列求和公式Sn=n(a1+an)2=na1+n(n1)2d,把所有大于等于x的项(即前t项)进行累加。同时还要注意到等于x数可能有多个,而在累加的时候会把所有等于x的也累加,因此还需要把多余的x减掉,方法是在遍历的时候开个变量cnt记录一共累加了多少项(也就是累加t),最后再对答案减去(cntm)x即可。

  注意由于这n组数可能无法构成m个以上的数,因此可能无法取到第m个数,因此二分的左边界设为0,当出现这种情况时二分得到的结果必然是0,可以发现按照上面累加总和的方法得到的结果还是对的(也就是把全部的数累加)。

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 1e5 + 10;
 7 
 8 int n, m;
 9 int a[N], b[N];
10 
11 bool check(int mid) {
12     LL cnt = 0;
13     for (int i = 0; i < n; i++) {
14         if (a[i] >= mid) cnt += (a[i] - mid) / b[i] + 1;    // 求有多少个大于等于mid的项
15     }
16     return cnt >= m;
17 }
18 
19 int main() {
20     scanf("%d %d", &n, &m);
21     for (int i = 0; i < n; i++) {
22         scanf("%d %d", a + i, b + i);
23     }
24     int l = 0, r = 1e6;
25     while (l < r) {
26         int mid = l + r + 1 >> 1;
27         if (check(mid)) l = mid;
28         else r = mid - 1;
29     }
30     LL ret = 0, cnt = 0;
31     for (int i = 0; i < n; i++) {
32         if (a[i] >= l) {
33             LL t = (a[i] - l) / b[i] + 1;   // 求有多少项大于等于第m大数x
34             cnt += t;   // 累加了多少项
35             ret += t * a[i] - t * (t - 1) / 2 * b[i];   // 求前t项的总和
36         }
37     }
38     printf("%lld", ret - (cnt - m) * l);    // 减去多累加的x
39     
40     return 0;
41 }
复制代码

 

参考资料

  AcWing 4656. 技能升级(寒假每日一题2023):https://www.acwing.com/video/4594/

posted @   onlyblues  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示