【Codeforces613B】Skills【二分】【枚举】【贪心】

http://codeforces.com/problemset/problem/613/B

题意:

有n个数,可以将其中一个数字加一,最多操作m次。给出最大值A,给出cf,cm。设等于A的数的个数为k,最小值为min,那么答案为k * cf + min * cm。求最大的答案,并输出最后的n个数。


排序,从大的一端枚举将多少数变为A,二分最小值,然后在小的一端二分查找可以使多少数变为最小值,更新答案。


懵逼,二分写搓了真容易被卡。


/* Footprints In The Blood Soaked Snow */
#include <cstdio>
#include <utility>
#include <algorithm>

using namespace std;

typedef long long LL;

typedef pair<int, int> pii;

const int maxn = 100005;

int n, A, cf, cm, val[maxn];
LL m, sum[maxn];

pii num[maxn];

inline bool check(int x, LL left, int mr) {
	int l = 1, r = mr;
	while(l <= r) {
		int mid = l + r >> 1;
		if(num[mid].first <= x) l = mid + 1;
		else r = mid - 1;
	}
	return (LL)r * x - sum[r] <= left;
}

int main() {
	scanf("%d%d%d%d%I64d", &n, &A, &cf, &cm, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &num[i].first);
		num[i].second = i;
	}

	sort(num + 1, num + 1 + n);
	for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + num[i].first;

	LL ans = -1, maxtot = 0, minval = 0;
	for(int i = 0; i <= n; i++) {
		LL left = m - ((LL)i * A - (sum[n] - sum[n - i]));
		if(left < 0) break;
		int l = 0, r = A;
		while(l <= r) {
			int mid = l + r >> 1;
			if(check(mid, left, n - i)) l = mid + 1;
			else r = mid - 1;
		}
		LL res = (LL)i * cf + (LL)r * cm;
		if(res > ans) {
			ans = res;
			maxtot = i;
			minval = r;
		}
	}

	for(int i = 1; i <= n; i++)
		if(n - maxtot < i) val[num[i].second] = A;
		else if(num[i].first <= minval) val[num[i].second] = minval;
		else val[num[i].second] = num[i].first;

	printf("%I64d\n", ans);
	for(int i = 1; i <= n; i++) printf("%d ", val[i]);
	return 0;
}


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