CF1875D

发布时间 2023-11-04 23:23:10作者: lucky_cloud

很简单的题。虽然没在考场上做出来

分析

我们经过思考,容易得出以下结论:

  1. 如果当前 $mex = x$,则下一个删的数一定小于 $x$。
  2. 如果 $mex = 0$,那么我们就可以不往下算了,因为它们对答案的贡献为 $0$。

我们设 $f[i]$ 表示当 $mex = i$ 时,$m$ 的值。

则有:

$$f[i] = \min(f[j] + (c[i] - 1) \times j + i, f[i])$$

其中 $j > i$,$c[i]$ 表示 $i$ 在 $a$ 中的个数。

因为,我们要使 $mex = i$,就必须将 $i$ 这个数删去,并且 $0 \sim i-1$ 都还存在于 $a$ 中。我们会删 $c[i]$ 次,但 $c[i] - 1$ 次,$m$ 会加上上一个 $mex$ 的值。 第 $c[i]$ 次则会加上 $i$,也就是新的 $mex$。

设没删任何数的 $mex = first$。

根据定义,初始化 $f[first] = 0$。

根据上述结论,与定义,答案即为 $f[0]$。

Code

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll 
using namespace std;

const int N = 5e3 + 5;
int t, x, p;
ll c[N], f[N];
ll pre, now, n;
 
int main() {
    ios::sync_with_stdio(0);
    cin >> t;
    while (t--) {
        cin >> n;
        ll mx = 0;
        memset(c, 0, sizeof c);
        memset(f, 0x3f, sizeof f);
        for (int i = 1; i <= n; i++) {
            ll x;
            cin >> x;
            if (x <= 5001) c[x]++, mx = max(mx, x);//因为至多 5000 个数,但数可能大于 5000。
        }
        for (int i = 0; i <= mx + 1; i++) {//找到没删任何数时的 mex。
            if (!c[i]) {
                p = i;
                break;
            }
        }
        f[p] = 0;
        for (int i = p; i >= 0; i--) {
            for (int j = i - 1; j >= 0; j--) {
                f[j] = min(f[i] + (c[j] - 1) * i + j, f[j]);
            }
        }
        cout << f[0] << '\n';
    }
    return 0;
}