VMC 组队

组队

image

问最少的队伍的人数最大值,典型的二分,唯一的难点在于如何 check 答案。

由于不能确定一个队几个人更优,所以先保证能组成人数最少的队伍(即一个队 mid 个人),并记录以能力值 \(a_i\) 结尾的队伍数量,用于判断“散户”能否成功入队。

数据处理上需要对原 \(a_i\) 数组进行离散化,开一个权值的桶用于记录每个离散化后的能力值有多少人,方便能力值相同者的处理。check 时按能力值对桶进行扫描,使组队必须以当前枚举的 \(i\) 为队伍的首个成员。

由于要求 \(i\)\(i+mid-1\) 的区间最小值,因此需要一棵支持区间减法,区间查询最小值的线段树。

代码如下:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read();
const int N = 3e5 + 5;
int n, a[N], b[N], Finish[N];
namespace Segment_Tree {
    const int M = N << 2;
    #define ls x << 1
    #define rs x << 1 | 1
    int a[M], n, lz[M];
    void push_up(int x) {
        a[x] = min(a[ls], a[rs]);
    }
    void push_down(int x) {
        if (!lz[x]) return;
        lz[ls] += lz[x], lz[rs] += lz[x];
        a[ls] -= lz[x], a[rs] -= lz[x];
        lz[x] = 0;
    }
    void build(int x = 1, int l = 1, int r = n) {
        lz[x] = 0;
        if (l == r) {
            a[x] = b[l];
            return;
        }
        int mid = l + r >> 1;
        build(ls, l, mid);
        build(rs, mid + 1, r);
        push_up(x);
    }
    int query_Min(int x, int ql, int qr, int l = 1, int r = n) {
        if (ql <= l and r <= qr) {
            return a[x];
        }
        push_down(x);
        int mid = l + r >> 1, ans = 1e9;
        if (ql <= mid) ans = min(ans, query_Min(ls, ql, qr, l, mid));
        if (qr > mid) ans = min(ans, query_Min(rs, ql, qr, mid + 1, r));
        return ans;
    }
    void upd(int x, int ql, int qr, int minus, int l = 1, int r = n) {
        if (ql <= l and r <= qr) {
            lz[x] += minus;
            a[x] -= minus;
            return;
        }
        push_down(x);
        int mid = l + r >> 1;
        if (ql <= mid) upd(ls, ql, qr, minus, l, mid);
        if (qr > mid) upd(rs, ql, qr, minus, mid + 1, r);
        push_up(x);
    }
} // namespace Segment_Tree
bool check(int mid, int Mx) {
    Segment_Tree::build();
    for (int i = 1; i <= Mx; ++i) Finish[i] = 0;
    for (int i = 1; i <= Mx; ++i) {
        if (i + mid - 1 <= Mx) {
            int Mi = Segment_Tree::query_Min(1, i, i + mid - 1);
            Segment_Tree::upd(1, i, i + mid - 1, Mi);
            Finish[i + mid - 1] += Mi;
        }
        int newA = Segment_Tree::query_Min(1, i, i);
        if (newA > Finish[i - 1])
            return false;
        Finish[i] += newA;
    }
    return true;
}
int main() {
    n = read();
    for (int i = 1; i <= n; ++i) {
        a[i] = read();
    }
    sort(a + 1, a + n + 1);
    int cnt = 1;
    b[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (a[i] - a[i - 1] > 1) cnt += 2;
        if (a[i] - a[i - 1] == 1) cnt++;
        ++b[cnt];
    }

    Segment_Tree::n = cnt;
    int l = 1, r = n, ans = 0;
    while (l <= r) {
        int mid = l + r >> 1;
        check(mid, cnt) ? l = mid + 1, ans = mid : r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}
inline int read() {
    int x = 0;
    char c = getchar();
    bool f = 1;
    while (!isdigit(c))
        f = c ^ 45, c = getchar();
    while (isdigit(c))
        x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return f ? x : -x;
}
posted @ 2025-02-18 18:27  Maplisky  阅读(12)  评论(0)    收藏  举报