CF1861F Four Suits

题意

\(n\) 个人,\(4\) 种牌。

\(i\) 个人有第 \(j\) 种牌 \(a_{i, j}\) 张。

你有第 \(j\) 种牌 \(b_j\) 张。

你需要把你手上的所有牌分给所有人,使得所有人牌的数量相同。

设一种分牌的方案的优美度为所有人最多的种类的牌数 \(x\),以及次多的牌数 \(y\),优美度即 \(x - y\)

你需要输出每一个人作为 \(x\) 时的最大优美度。

\(n \le 5 \times 10 ^ 4\),保证一定有解。

Sol

考虑枚举当前的人与种类。

\(m\) 表示每个人最终的牌数,\(lim_i\) 表示第 \(i\) 个人距离最终的牌数的差值。

一个显然的结论,若我们已经确定了最多的牌堆,显然该堆增加的牌数为 \(min(b_j, lim_i - a_{i, j})\)

证明很简单,设当前最多的牌堆为 \(P\),若我们把放到其他牌堆的 \(j\) 种牌与 \(j'\) 种牌交换,最终答案一定不劣。

很容易想到二分一个次大值的上限 \(k\)

考虑一个 \(\text{flow}\)

  • \(S \to {B_1, B_2, B_3, B_4}\),容量为 \(b_j\)
  • \(B_j \to a_i\),容量为 \(k - a_{i, j}\)
  • \(a_i \to T\),容量为 \(lim_i\)

套路的,考虑转最小割。

连源点的边只有 \(4\) 条,直接 \(2 ^ 4\) 暴枚。

设当前选择不选择的边集为 \(s\)

则每个人 \(i\) 的贡献为 \(min(|S| \times k - \sum_{j \in s} a_j, lim_i)\)

后面这个 \(lim_i\) 是不变的,前面只和 \(k\) 有关。

所以我们可以找到一个分界点,用两个数组直接差分预处理出来。

存一下后面这一坨和 \(k\) 的个数就行了。

每次 \(\text{check}\) 的时候,把当前的割算出来,减去当前选中的点的贡献即可。

由于是求最小割,保留判断最小值是否 \(\ge\) 最大流即可。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#define int long long
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
    int p = 0, flg = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') flg = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        p = p * 10 + c - '0';
        c = getchar();
    }
    return p * flg;
}
void write(int x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}
bool _stmer;

const int N = 5e4 + 5, M = 4e6 + 5, V = 4e6, inf = 4e18;

array <array <int, 5>, N> s;
array <int, 5> arc;

array <int, N> lim, isl;

array <array <signed, M>, 16> bk, ck;

bool check(int x, int i, int mflow) {
    int mcut = inf;
    for (int T = 0; T < 16; T++) {
        int len = 0, tp0 = 0, res = 0;
        for (int k = 0; k < 4; k++) {
            if (!(T & (1 << k))) res += arc[k + 1];
            else len++, tp0 += s[i][k + 1];
        }
        res += bk[T][x] + 1ll * ck[T][x] * x * len - min(lim[i], len * x - tp0);
        /* cerr << ck[T][x] << endl; */
        mcut = min(mcut, res);
    }
    /* cerr << mcut << endl; */
    return mcut >= mflow - lim[i];
}

bool _edmer;
signed main() {
    cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
    int n = read(), m = 0;
    int tp0 = 0, tp1 = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 4; j++)
            s[i][j] = read(), m += s[i][j], isl[i] = max(s[i][j], isl[i]);
        if (isl[i] > tp0) tp1 = tp0, tp0 = isl[i];
        else tp1 = max(tp1, isl[i]);
    }
    for (int i = 1; i <= 4; i++)
        arc[i] = read(), m += arc[i];
    m /= n;
    int mflow = 0;
    for (int i = 1; i <= n; i++) {
        lim[i] = m;
        for (int j = 1; j <= 4; j++)
            lim[i] -= s[i][j];
        mflow += lim[i];
    }
    for (int T = 1; T < 16; T++) {
        int len = 0;
        for (int i = 0; i < 4; i++) len += (bool)(T & (1 << i));
        for (int i = 1; i <= n; i++) {
            int res = 0;
            for (int j = 0; j < 4; j++)
                if (T & (1 << j))
                    res += s[i][j + 1];
            int pos = (lim[i] + res) / len;
            bk[T][0] -= res, bk[T][pos + 1] += res;
            bk[T][pos + 1] += lim[i], ck[T][0]++, ck[T][pos + 1]--;
        }
        for (int i = 1; i <= V; i++)
            bk[T][i] += bk[T][i - 1], ck[T][i] += ck[T][i - 1];
    }
    for (int i = 1; i <= n; i++) {
        int tpl = isl[i] == tp0 ? tp1 : tp0, _ans = 0;
        for (int j = 1; j <= 4; j++) {
            int tp2 = min(arc[j], lim[i]);
            arc[j] -= tp2;
            int l = tpl, r = s[i][j] + tp2;
            int ans = -1;
            /* cerr << check(1, i, mflow) << endl; */
            /* exit(0); */
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (check(mid, i, mflow)) ans = mid, r = mid - 1;
                else l = mid + 1;
            }
            if (~ans) _ans = max(_ans, s[i][j] + tp2 - ans);
            arc[j] += tp2;
        }
        write(_ans), putchar(32);
    }
    puts("");
    return 0;
}
posted @ 2024-06-18 22:18  cxqghzj  阅读(5)  评论(0编辑  收藏  举报