传送门:点击打开链接
题意:n个科目,每个科目最高分为A,现在最多能提高m分。最后权值是两种分数算法之和,第一种算法是达到A分的科目个数*cf,第二种算法是最低分*cm。求提高单科分数之后,最后的权值最大是多少。并打印出方案。
思路:枚举达到A分的科目数,然后再二分得到剩下的钱,让最低分最大是多少。
首先,肯定要把科目的所有分数排序一次,然后枚举达到满分的科目时,肯定是从大到小去枚举的。
然后再看剩下的钱数,去二分m,m表示最低分最大是多少。那么问题在check上,然后处理好check就行了~
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iomanip> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int>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; }