模拟赛 T1 好做法

不服来叉

首先肯定要二分答案 \(k\),然后肯定留最大的 \(k\) 个数,

考虑删除剩下 \(n-k\) 个数的策略,肯定是倒着删,考察一下删除倒数第 \(i\) 个数的时候到底发生了什么,

这里我们先假设之前 \(i-1\) 个数可以成功删掉,否则可以在前面判断出不能删,

不妨考虑在删之前 \(i-1\) 个数的时候,如果 \(x\) 有法力值分给了这 \(i\) 个数中的 \(k\),视为 \(x\)\(k\) "进了一位",

那我们就要使得之前 \(i-1\) 个数对 \(i\) 的"进位",也就是分给 \(i\) 的法力值尽量少,

显然有策略:对每个数 \(x\),先把法力值分给所有不会产生进位的 \(n-i\) 个数,剩余的法力值留下来进位,

可以看成是填一个 \(i\)\(n-i\) 列的矩阵,每次填一行,多余的法力值留到之后的行,

那这样留到最后一行的进位个数就是 \(\sum\limits_{x=1}^{i-1}v_x-(i-1)(n-i)\)

那么 \(i\) 需要分出去的法力值就是 \(v_i\) 加上进位,也就是 \(\sum\limits_{x=1}^iv_x-(i-1)(n-i)\)

\(i\) 只能把法力值分给剩下 \(n-i\) 个数,所以 \(\sum\limits_{x=1}^iv_x-(i-1)(n-i)\le n-i\),如果满足条件那 \(i\) 就能被删掉,否则 \(i\) 删不掉。

#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
int T, n, a[5000050];
inline int R()
{
    int q = 0;
    char c = getchar();
    while (c < '0' || c > '9')
        c = getchar();
    while (c >= '0' && c <= '9')
        q = q * 10 + c - '0', c = getchar();
    return q;
}
bool C(int k)
{
    int z = 0;
    for (int i = n; i > k; --i)
    {
        z += a[i - k];
        if (z > (n - i + 1) * (i - 1))
            return 0;
    }
    return 1;
}
signed main()
{
    freopen("magic.in", "r", stdin);
    freopen("magic.out", "w", stdout);
    T = R();
    while (T--)
    {
        n = R();
        for (int i = 1; i <= n; ++i)
            a[i] = R();
        sort(a + 1, a + n + 1);
        int l = 1, r = n;
        while (l <= r)
        {
            int m = l + r >> 1;
            if (C(m))
                r = m - 1;
            else
                l = m + 1;
        }
        printf("%lld\n", l);
        for (int i = 1; i <= n; ++i)
            a[i] = 0;
    }
    return 0;
}
posted @ 2024-05-26 19:53  5k_sync_closer  阅读(42)  评论(0编辑  收藏  举报