蓝桥杯省赛技能升级|二分+优先队列做法

题目描述:

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

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

⌈Ai/Bi⌉次之后,再升级该技能将不会改变攻击力。

总计可以升级M次技能,可以任意选择升级的技能和次数。

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

输入:

N,M(1<=N<=1e5,1<=M<=2e9)

接下来N行每行为Ai,Bi(1<=Ai,Bi<=1e6)

输出:

输出最大攻击力

Solution:

(二分+优先队列) ≈O(nlogn)

我们先设定lowbound为当所有a[i]大于等于lowbound的技能加点至a[i]小于等于lowbound,同时设定此时消耗的次数为mt,获得的攻击力为res。

此时我们现在就可以以mt是否小于等于m为条件二分出0~max(a[1~n])的区间中最大的lowbound和mt,且此时|mt-m|<n(因为如果|mt-m|>=n的话,就会一定存在更好的lowbound,而这与二分得到的事实相悖)。

当时这个时候我们得到的mt大概率并不是最终答案,

此时我们就用一个优先队列去储存所以a[i]加点至小于等于lowbound的状态,依此取出队头,对其进行一次加点,然后再入队,同时mt++,直至mt==m,输出res。

时间复杂度

二分复杂度:

O(nlog(max(a[1~n])))

优先队列复杂:

由于|mt-m|<n的,故优先队列的复杂度为O(nlogn)

整体复杂度就为O((nlog(max(a[1~n]))+nlogn)

而max(a[1~n])<=1e6,n<=1e5,最终复杂度便≈O(nlogn)

C++ Code

#include <bits/stdc++.h>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef pair<ll, ll>PII;
const ll N = 1e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const double eps = 1e-6;
ll n, m;
ll a[N], b[N];
ll mt = 0, res = 0;
bool check(ll mid) {
    ll t = 0;
    for (ll i = 1; i <= n; i++) {
        if (a[i] <= mid)continue;
        ll c = (a[i] - mid + b[i] - 1) / b[i];
        t += c;
    }
    if (t <= m) return true; 
    return false;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    ll maxv = 0;
    for (ll i = 1; i <= n; i++) {
        cin >> a[i] >> b[i];
        maxv = max(maxv, a[i]);
    }
    ll l = 0, r = maxv;
    while (l < r) {//二分
        ll mid = l + r >> 1;
        if (check(mid))r = mid;
        else l = mid + 1;
    }
    priority_queue<PII, vector<PII>>heap;
    for (ll i = 1; i <= n; i++) {//计算二分后至少加的攻击力
        if (a[i] <= r) {
            heap.push({ a[i],b[i] });
            continue;
        }
        ll c = (a[i] - r + b[i] - 1) / b[i];
        ll y = a[i] % b[i];
        mt += c;
        if (a[i] - c * b[i] < 0) res += (y + a[i]) * c / 2;
        else res += (2 * a[i] - (c-1) * b[i]) * c / 2;
        a[i] = max((ll)0, a[i] - c * b[i]);
        heap.push({ a[i],b[i] });
    }
    while (mt < m) {//计算剩余可加的攻击力
        auto t = heap.top();
        heap.pop();
        if (t.first <= 0)break;
        res += t.first;
        t.first -= t.second;
        mt++;
        heap.push(t);
    }
    cout << res << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值