CF1875D

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

分析

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

  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;
} 
posted @ 2023-11-04 23:17  lucky_cloud  阅读(7)  评论(0编辑  收藏  举报