[蓝桥杯2022初赛C组] 技能升级(二分答案*)

思路:

    比较特别的二分答案题。通常的思路是题目要求什么就二分答案什么。但此题二分 最多能提高多少攻击力 是不可行的,因为根据这个 “答案” 你没法确定每个技能升级多少次,跟 M 无法联系,也就不存在 check 关系。 正解是二分 最后一次升级技能提升了多少攻击力。 我们升级技能的朴素过程,肯定是贪心的思路,找到所有技能中攻击力最高的,升级它,再改变它的点数。可以发现,当我们知道了最后一次升级技能提升了多少攻击力,由于之前升级技能的贪心过程,之前升级技能提升的攻击力都不少于 该次,而升级技能点数减少的过程是一个等差数列,我们显然能 O ( 1 ) O(1) O(1) 的时间知道每个技能升级了几次。最后根据升级技能的次数和 M 对比,check 关系就出来了。 知道了 每个技能升级的次数,显然也可以推导出答案最多可以提高多少点攻击力。 还有些细节问题:check 时,累计的是升级技能攻击力 大于 mid 的次数。算答案时,先累计优先升级比最后一次攻击力高的,再累计相同的。

C o d e : Code: Code:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define debug(x) cout<<"target is "<<x<<endl
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define all(a) a.begin(),a.end()
#define oper(a) operator<(const a& ee)const
#define endl "
"
#define ul (u << 1)
#define ur (u << 1 | 1)
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

const int N = 100010, M = 200010, MM = 110;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k;
ll a[N], b[N];

bool check(int mid) {
          
   
    ll sum = 0;//统计次数
    for (int i = 1; i <= n; i++) {
          
   
        if (a[i] <= mid)continue; //不统计攻击力等于 mid 的升级
        sum += (a[i] - mid - 1) / b[i] + 1;
    }
    return sum > m;
    //大于 m 时是不可取的,小于等于逼近
}

ll cot(ll be, ll ed, ll xi) {
          
   
    return (be + ed) * xi / 2;
}

ll cal(int mi) {
          
    //计算答案
    int summi = 0;//统计攻击力等于 mid 的升级的次数
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
          
   
        if (a[i] < mi)continue;
        if ((a[i] - mi) % b[i] == 0)summi++;
        if (a[i] == mi)continue;
        //贪心,一开始 sum 先加攻击力大于 mid 的升级

        //首项末项项数
        ll xi = (a[i] - mi - 1) / b[i] + 1;
        ll ed = a[i] - (xi - 1) * b[i];
        sum += cot(a[i], ed, xi);
        m -= xi;
    }

    //如果还有多余的升级次数,再升级 攻击力等于 mid 的技能
    while (m && summi) {
          
   
        m--; summi--;
        sum += mi;
    }
    return sum;
}

void solve() {
          
   
    cin >> n >> m;
    forr(i, 1, n) {
          
   
        cin >> a[i] >> b[i];
    }
    
    //二分 最后一次升级技能提升了多少攻击力
    int l = 1, r = 1e6 + 10;
    while (l < r)
    {
          
   
        int mid = l + r >> 1;
        if (check(mid))l = mid + 1;
        else r = mid;
    }

    cout << cal(l);
}

int main() {
          
   
    cinios;
    int T = 1;
    forr(t, 1, T) {
          
   
        solve();
    }
    return 0;
}
经验分享 程序员 微信小程序 职场和发展