多路归并+二分

acwing - 技能升级

题目描述

他的角色一共有 N 个可以加攻击力的技能。其中第i个技能首次升级可以提升Ai点攻击力,以后每次升级增加的点数都会减少Bi

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

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

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

数据范围

输入样例

3 6
10 5
9 2
8 1

输出样例

47

为什么用二分

根据题中给出的数据,可判断时间复杂度要控制在O(NlogN)以内,同时将0 ~ 1e6的数从小到大排列,若 >=mid 的数有 >=m 个,则在mid的左边一定成立,mid的右边一定不成立,具有二段性。

key code

#include <iostream>
using namespace std;

const int N = 1e5 + 10;

typedef long long LL;

int a[N], b[N];
int n, m;

bool check(int mid)
{
    LL res = 0;
    for(int i = 1; i <= n; i ++)
        if(a[i] >= mid)             //枚举每一个序列,如果a[i] >= mid,则在该序列中有满足条件的数
            res += (a[i] - mid) / b[i] + 1;   //加上 >= mid 的数的个数
    return res >= m;         //如果个数 >= m,则答案在 mid 右边,l = mid,否则 r = mid - 1
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
        cin >> a[i] >> b[i];
    
    int l = 0, r = 1e6;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;        //将0 ~ 1e6的数从小到大排列,mid为其中的数值
        else r = mid - 1;
    }
    
    LL cnt = 0, res = 0;
    for(int i = 1; i <= n; i ++)
    {
        if(a[i] >= r) 
        {
            int c = (a[i] - r) / b[i] + 1;   //项数
            int end = a[i] - (c - 1) * b[i];   //末项
            cnt += c;                          //总的项数
            res += (LL)(a[i] + end) * c / 2;     //每个序列中满足条件的项的和
        }
    }
    
    cout << res - (cnt - m) * r << endl;    //如果r有多项,则只取规定项数内的,减去多余项的和
    return 0;
}

 

posted @   breeze_ku  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示