[BZOJ4868][Shoi2017]期末考试(暴力)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=4868

Solution

由于期末考试中考的到来,写了这一题。
先把问题抽象化:
n 个数,分别为 t 1 , t 2 , . . . , t n
还有 m 个数,分别为 b 1 , b 2 , . . . , b m
操作一:对于一对 i j ,将 b i 加一, b j 减一的代价为 A
操作二:对于一个 i ,将一个 b i 减一的代价为 B
上面两种操作可以执行多次。
操作执行完之后,对于任何一个 i ,如果 max { b } > t i ,则产生的代价为 max { b } t i
求最小代价和。
把问题拆开,先对于每个 1 k max { b } ,求出使 max { b } 下降到 k 的最小代价和。
显然,如果 A B ,那么操作一是没有意义的。最小代价和为

i = 1 m max ( b i k , 0 )

b 排序后可以求出。
否则可能既有操作一又有操作二(当然操作一要多用)。设 b i < b j ,那么一定有 b i < k < b j
也就是说,操作一的 i 一定满足 b i < k j 一定满足 b j > k
满足 b i < k b 值最多可以加一 x = 1 m max ( k b x , 0 ) 次。
满足 b j > k b 值最多可以减一 x = 1 m max ( b x k , 0 ) 次。
所以,这样操作一的执行次数为:
min ( x = 1 m max ( k b x , 0 ) , x = 1 m max ( b x k , 0 ) )

操作二的执行次数:
| x = 1 m max ( k b x , 0 ) x = 1 m max ( b x k , 0 ) |

求出将 max { b } 降到 k 的最小代价和后,再加上 x = 1 n max ( k t x , 0 ) ,就可以更新答案。
同样将 t 排序。
特判: C = 10 16 时,只有 max { b } < min { t } 才能保证最优。为了不爆 long long ,只需要将 k 1 枚举到 min { t } 而不是 10 5 即可。

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;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80808652