bzoj 4868 [Shoi2017]期末考试 三分+贪心

题面

题目传送门

解法

先不妨枚举最后在第\(x\)天成绩全部出来

那么,可以计算出有至少需要\(s_1\)次减少老师,\(s_2\)次增加老师

如果\(A>B\),那么显然每一次都做\(B\)这个操作,否则先尽量用\(A\),然后如果还有剩余那么用\(B\)

把每一个\(x\)对应出来计算的值定义为\(f(x)\)

可以发现,\(f\)是一个单峰函数,我也不知道为什么

然后三分求函数极值即可

时间复杂度:\(O(n\ log\ n)\)

代码

#include <bits/stdc++.h>
#define int long long
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int A, B, C, n, m, t[N], b[N];
int check(int mid) {
	int ret = 0;
	int s1 = 0, s2 = 0;
	for (int i = 1; i <= m; i++)
		if (b[i] < mid) s1 += mid - b[i]; else s2 += b[i] - mid;
	if (A > B) ret += B * s2;
		else if (s1 < s2) ret += A * s1 + B * (s2 - s1);
			else ret += A * s2;
	for (int i = 1; i <= n; i++)
		if (t[i] < mid) {
			if (C == 1e16) {ret = LONG_LONG_MAX; break;}
			ret += C * (mid - t[i]);
		}
	return ret;
}
main() {
	read(A), read(B), read(C), read(n), read(m);
	int l = INT_MAX, r = -INT_MAX;
	for (int i = 1; i <= n; i++) read(t[i]);
	for (int i = 1; i <= m; i++)
		read(b[i]), chkmin(l, b[i]), chkmax(r, b[i]);
	while (l + 5 < r) {
		int mid1 = l + (r - l) / 3, mid2 = l + (r - l) / 3 * 2;
		if (check(mid1) < check(mid2)) r = mid2;
			else l = mid1;
	}
	int ans = LONG_LONG_MAX;
	for (int i = l; i <= r; i++)
		chkmin(ans, check(i));
	cout << ans << "\n";
	return 0;
}

posted @ 2018-08-14 18:25  谜のNOIP  阅读(150)  评论(0编辑  收藏  举报