Address
https://www.lydsy.com/JudgeOnline/problem.php?id=4868
Solution
由于期末考试中考的到来,写了这一题。
先把问题抽象化:
有
个数,分别为
。
还有
个数,分别为
。
操作一:对于一对
,将
加一,
减一的代价为
。
操作二:对于一个
,将一个
减一的代价为
。
上面两种操作可以执行多次。
操作执行完之后,对于任何一个
,如果
,则产生的代价为
。
求最小代价和。
把问题拆开,先对于每个
,求出使
下降到
的最小代价和。
显然,如果
,那么操作一是没有意义的。最小代价和为
将 排序后可以求出。
否则可能既有操作一又有操作二(当然操作一要多用)。设 ,那么一定有 。
也就是说,操作一的 一定满足 , 一定满足 。
满足 的 值最多可以加一 次。
满足 的 值最多可以减一 次。
所以,这样操作一的执行次数为:
操作二的执行次数:
求出将 降到 的最小代价和后,再加上 ,就可以更新答案。
同样将 排序。
特判: 时,只有 才能保证最优。为了不爆 long long ,只需要将 从 枚举到 而不是 即可。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 1e5 + 5;
int A, B, n, m, t[N], b[N], cntt[N], cntl[N], cntr[N], w1[N], w2[N], MaxN = 1e5;
ll C, sumt[N], suml[N], sumr[N], f[N], Ans = ((1ll << 62) - 1 << 1) + 1;
int main() {
int i, sp = 1; A = read(); B = read(); cin >> C; n = read(); m = read();
For (i, 1, n) w1[t[i] = read()]++; For (i, 1, m) w2[b[i] = read()]++;
For (i, 1, MaxN) cntt[i] = cntt[i - 1] + w1[i], cntl[i] = cntl[i - 1] + w2[i],
sumt[i] = sumt[i - 1] + 1ll * w1[i] * i,
suml[i] = suml[i - 1] + 1ll * w2[i] * i; Rof (i, MaxN, 1)
cntr[i] = cntr[i + 1] + w2[i], sumr[i] = sumr[i + 1] + 1ll * w2[i] * i;
while (!w2[MaxN]) MaxN--; For (i, 1, MaxN) {
ll le = 1ll * i * cntl[i] - suml[i], ri = sumr[i] - 1ll * i * cntr[i];
if (A >= B) f[i] = 1ll * ri * B; else if (le >= ri) f[i] = 1ll * ri * A;
else f[i] = 1ll * le * A + 1ll * (ri - le) * B;
}
while (!w1[sp]) sp++; For (i, 1, (C == 1e16 ? min(sp, MaxN) : MaxN))
Ans = min(Ans, f[i] + (1ll * i * cntt[i] - sumt[i]) * C);
cout << Ans << endl; return 0;
}