蓝桥杯-第十三届省赛真题-技能升级(二分 + 优先队列)

链接:第十三届省赛真题-技能升级

题意:
蓝桥杯-第十三届省赛真题-技能升级(二分 + 优先队列)_第1张图片
思路:

  1. 前m次技能升级的收益肯定是最大的m个,所以我们可以二分最后一次升级获得的收益。如果获得比当前收益大的所有次数要大于m,我们就往右边找,否则往左找。
  2. 需要注意的是,二分出来的边界不一定会保证刚好升级m次,我们保证它小于等于m次。最后剩余的次数用优先队列暴力跑一下就行了。

代码:

#include
using namespace std;
const int maxn = 2e6 + 7;
typedef long long ll;
const int mod = 998244353;
ll n , a[maxn] ,b[maxn] , m;
int res;
priority_queue< pair<int ,int > >que;
ll cal(int mid){
    ll ans = 0;
    res = m;
    for(int i = 1; i <= n; i ++){
        if(a[i] < mid) continue;
        int cnt = (a[i] - mid) / b[i] + 1;
        ans += (a[i] + a[i] - (cnt - 1) * b[i]) * cnt / 2;
        a[i] -= b[i] * cnt;
        res -= cnt;
    }
    return ans;
}
ll check1(int mid){
    ll ans = 0;
    for(int i = 1; i <= n; i ++){
        if(a[i] < mid) continue;
        int cnt = (a[i] - mid) / b[i] + 1;
        ans += cnt;
    }
    return ans <= m;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i = 1; i <= n; i ++){
        scanf("%lld%lld",&a[i],&b[i]);
    }
    int l = 1,r = 1e6,mid,ans;
    while(l <= r){
        mid = (l + r) / 2;
        if(check1(mid)){
            r = mid - 1;
            ans = mid;
        }
        else{
            l = mid + 1;
        }
    }
    ll pr = cal(ans);
    for(int i = 1; i <= n; i ++){
        que.push({a[i] , b[i]});
    }
    while(res--){
        pair<int ,int> now = que.top();
        que.pop();
        if(now.first <= 0) break;
        pr += now.first;
        now.first = max(0 , now.first - now.second);
        que.push(now);
    }
    printf ("%lld\n",pr);
    return 0;
}




你可能感兴趣的:(蓝桥杯)