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

TP

[蓝桥杯2022初赛C组] 技能升级(二分答案*)_第1张图片

思路:

  • 比较特别的二分答案题。通常的思路是题目要求什么就二分答案什么。但此题二分 最多能提高多少攻击力 是不可行的,因为根据这个 “答案” 你没法确定每个技能升级多少次,跟 M 无法联系,也就不存在 check 关系。

  • 正解是二分 最后一次升级技能提升了多少攻击力

  • 我们升级技能的朴素过程,肯定是贪心的思路,找到所有技能中攻击力最高的,升级它,再改变它的点数。可以发现,当我们知道了最后一次升级技能提升了多少攻击力,由于之前升级技能的贪心过程,之前升级技能提升的攻击力都不少于 该次,而升级技能点数减少的过程是一个等差数列,我们显然能 O ( 1 ) O(1) O(1) 的时间知道每个技能升级了几次。最后根据升级技能的次数和 M 对比,check 关系就出来了。

  • 知道了 每个技能升级的次数,显然也可以推导出答案最多可以提高多少点攻击力。

  • 还有些细节问题:check 时,累计的是升级技能攻击力 大于 mid 的次数。算答案时,先累计优先升级比最后一次攻击力高的,再累计相同的。

C o d e : Code: Code:

#include
#include
#include
#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 "\n"
#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;
}

你可能感兴趣的:(二分,贪心算法,算法,二分,数学,蓝桥杯)