CF1684E MEX vs DIFF 题解

题目传送门(洛谷)

题目传送门(codeforces)

题意

给定 \(n\) 个元素的数组 \(a\),最多可修改 \(k\) 次,每次把数组中的一个值该为任意非负整数,求 \(\text{DIFF}(a)-\text{MEX}(a)\),其中 \(\text{DIFF}(a)\) 表示 \(a\) 数组中元素的种类数,\(\text{MEX}(a)\) 表示数组中没出现过的最小非负整数。

做法

首先根据式子,因为左连续中的种类数恰好是 \(\text{MEX}(a)\),所以所求的答案为修改后数组不包含在左连续内的元素的种类数最小值。然后就很容易想到贪心,想办法让这个值最小,有两种手段:

  1. 用不包含在左连续内的元素去填到左面;
  2. \(\text{MEX}(a)\) 扩大,让他包含的元素种类更多,覆盖其他元素。

继续分析,发现两种手段可以合并:求出能够得到的最大的 \(\text{MEX}(a)\),尽可能用比 \(\text{MEX}(a)\) 大的元素往左面填,并且尽可能用个数较多的填,以保证剩余的种类数最少,如果可以把比 \(\text{MEX}(a)\) 大的元素都用光,结果为 \(0\),否则结果为剩余的种类数。

很好的贪心思维题!

代码

// Problem: E. MEX vs DIFF
// Contest: Codeforces - Codeforces Round #792 (Div. 1 + Div. 2)
// URL: https://codeforc.es/contest/1684/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define int long long
#define db double
using namespace std;
namespace Std {
inline void maxy(int &x, int y) { x = max(x, y); }
inline void miny(int &x, int y) { x = min(x, y); }
inline int lowbit(int x) { return x & (-x); }
template <typename tp>
void read(tp &x) {
  x = 0;
  tp nev = 1;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') nev = -1;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = (x << 1) + (x << 3) + (c ^ 48);
    c = getchar();
  }
  x *= nev;
}
template <typename tp>
void write(tp x) {
  if (x < 0) {
    putchar('-');
    write(-x);
    return;
  }
  if (x / 10) write(x / 10);
  putchar((x % 10) ^ 48);
}
int t, n, now, num1, num2, a[200010], b[200010], c[200010], cnt, k;
bool mark[200010];
int main() {
  read(t);
  while (t--) {
    for (int i = 0; i < n; ++i) mark[i] = 0;
    a[0] = -1;
    read(n);
    read(k);
    cnt = 0;
    now = 0;
    for (int i = 1; i <= n; ++i) {
      read(a[i]);
    }
    sort(a + 1, a + 1 + n);
    num1 = 0;
    for (int i = 1; i <= n; ++i) {
      if (a[i] < n) {
        mark[a[i]] = 1;
      }
      if (a[i] != a[i - 1]) {
        b[++cnt] = a[i];
        c[cnt] = 1;
      } else {
        ++c[cnt];
      }
    }
    if (!k) {
      for (int j = 0; j < n; ++j) {
        if (!mark[j]) {
          num2 = j;
          break;
        }
      }
      printf("%lld\n", cnt - num2);
      continue;
    }
    int kk = k;
    for (int i = 0; i < n && kk; ++i) {
      if (!mark[i]) {
        --kk;
        mark[i] = 1;
      }
      if (!kk) {
        for (int j = i + 1; j <= n; ++j) {
          if (!mark[j]) {
            num2 = j - 1;
            break;
          }
        }
      }
      if (i == n - 1) num2 = n - 1;
    }
    if (num2 == n - 1)
      puts("0");
    else {
      num1 = 0;
      for (int i = 1; i <= cnt; ++i) {
        if (b[i] > num2) {
          num1 = i;
          break;
        }
      }
      if (!num1) {
        puts("0");
        continue;
      }
      sort(c + num1, c + 1 + cnt);
      int sum = 0;
      for (int i = num1; i <= cnt; ++i) sum += c[i];
      int ans = 0, nm = 0;
      for (int i = cnt; i >= num1; --i) {
        if (nm >= sum - k) break;
        ++ans, nm += c[i];
        if (nm >= sum - k) break;
      }
      printf("%lld\n", ans);
    }
  }
  return 0;
}
}  // namespace Std
#undef int
int main() { return Std::main(); }
posted @ 2022-05-21 23:57  Wilson_Inversion  阅读(48)  评论(0编辑  收藏  举报