二分+贪心 Codeforces614D Skills

传送门:点击打开链接

题意:n个科目,每个科目最高分为A,现在最多能提高m分。最后权值是两种分数算法之和,第一种算法是达到A分的科目个数*cf,第二种算法是最低分*cm。求提高单科分数之后,最后的权值最大是多少。并打印出方案。

思路:枚举达到A分的科目数,然后再二分得到剩下的钱,让最低分最大是多少。

首先,肯定要把科目的所有分数排序一次,然后枚举达到满分的科目时,肯定是从大到小去枚举的。

然后再看剩下的钱数,去二分m,m表示最低分最大是多少。那么问题在check上,然后处理好check就行了~

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fuck(x) cout<<"["<PII;

const int MX = 1e5 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

PII A[MX];
int W, cf, cm, n, prt[MX];
LL m, sum[MX];

bool check(LL m, int x, int p) {
    p = min(upper_bound(A + 1, A + 1 + n, PII(x, W)) - A - 1, p - 1);
    LL tot = (LL)p * x - sum[p];
    return tot <= m;
}

int get(LL money, int p) {
    int l = 1, r = W, m;
    if(!check(money, l, p)) return 0;
    while(l <= r) {
        m = (l + r) >> 1;
        if(check(money, m, p)) l = m + 1;
        else r = m - 1;
    }
    return l - 1;
}

int main() {
    //FIN;
    while(~scanf("%d%d%d%d%I64d", &n, &W, &cf, &cm, &m)) {
        sum[0] = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &A[i].first);
            A[i].second = i;
        }
        sort(A + 1, A + 1 + n);
        for(int i = 1; i <= n; i++) {
            sum[i] = sum[i - 1] + A[i].first;
        }

        int ansa = 0, ansb = get(m, n + 1);
        LL s = m, ans = (LL)ansb * cm;
        for(int i = n, j = 1; i >= 1; i--, j++) {
            s -= W - A[i].first;
            if(s < 0) break;

            int t = get(s, i);
            LL temp = (LL)j * cf + (LL)t * cm;
            if(temp > ans) {
                ans = temp; ansa = j; ansb = t;
            }
        }

        printf("%I64d\n", ans);
        for(int i = n, j = 1; i >= 1; i--, j++) {
            if(j <= ansa) prt[A[i].second] = W;
            else prt[A[i].second] = max(A[i].first, ansb);
        }
        for(int i = 1; i <= n; i++) {
            printf("%d%c", prt[i], i == n ? '\n' : ' ');
        }
    }
    return 0;
}


你可能感兴趣的:(ACM_二分)