Codeforces Round 887 (Div. 1) 题解

https://codeforces.com/contest/1852/problems

A. Ntarsis' Set

https://codeforces.com/contest/1852/problem/A

image

image

感觉不是很一眼。

\(n\)\(k\) 都是 \(2 \times 10^5\)不能暴力,设当前集合为 \({1, 2, \dots, 10^{1000}}\),那么被操作过一次的最小值就应该是 \(\text{MEX}(0, a_1, a_2, \dots, a_n)\)

扩展一下,集合 \(S\) 被操作过一次的最小值就是 \(S_{\text{MEX}(0, a_1, a_2, \dots, a_n)}\)(其中 \(S_k\)\(S\) 的第 \(k\) 小值)。

观察样例及其解释:


5 3
1 3 5 6 7
Day \(S\) Before \(S\) After
\(1\) \(\{\cancel 1, 2, \cancel 3, 4, \cancel 5, \cancel 6, \cancel 7, 8, 9, 10, \ldots \}\) \(\{2, 4, 8, 9, 10, \ldots\}\)
\(2\) \(\{\cancel 2, 4, \cancel 8, 9, \cancel{10}, \cancel{11}, \cancel{12}, 13, 14, 15, \ldots\}\) \(\{4, 9, 13, 14, 15, \ldots\}\)
\(3\) \(\{\cancel 4, 9, \cancel{13}, 14, \cancel{15}, \cancel{16}, \cancel{17}, 18, 19, 20, \ldots\}\) \(\{9, 14, 18, 19, 20, \ldots\}\)

设操作一次后的集合为 \(S\),那么最小值就是 \(S_1\),能保留的最小的也就是第 \(S_1\) 小的数,观察一下,下一个数就是 \(S\) 中第 \(S_1\) 小的数,所以这样递归算就行了。

具体实现见代码。

/**
  * author : OMG_78
  * created: 2023-07-24-09.19.39
 **/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
  if (c < '0') return true;
  if (c > '9') return true;
  return false;
}
template < class Z >
inline void read (Z &tmp) {
  Z x = 0, f = 0;
  char c = getchar ();
  for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
  for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
  tmp = !f ? x : -x;
}
int n, k, cnt;
ll a[200005], b[210005][2], sm[210005];
inline ll qu (ll x) {
  int L = 1, R = cnt;
  while (R - L > 1) {
    int mid = L + R >> 1;
    if (x >= sm[mid - 1] + 1) L = mid;
    else R = mid;
  }
  if (x >= sm[R - 1] + 1) {
    x -= sm[R - 1], x --;
    return b[R][0] + x;
  }
  else {
    x -= sm[L - 1], x --;
    return b[L][0] + x;
  }
}
bool Memory_Ends;
signed main () {
  fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
  int _; read (_);
  while (_ --) {
    read (n), read (k);
    for (int i = 1;i <= n; ++ i) read (a[i]);
    if (a[1] != 1) {
      printf ("1\n");
      continue;
    }
    cnt = 0;
    for (int i = 1;i <= n; ++ i) {
      if (i != n) {
        if (a[i] + 1 <= a[i + 1] - 1) cnt ++, b[cnt][0] = a[i] + 1, b[cnt][1] = a[i + 1] - 1;
      }
      else {
        cnt ++;
        b[cnt][0] = a[n] + 1, b[cnt][1] = 9e18;
      }
    }
    for (int i = 1;i <= cnt; ++ i) sm[i] = sm[i - 1] + b[i][1] - b[i][0] + 1;
    ll u = b[1][0];
    -- k;
    while (k --) {
      u = qu (u);
    }
    printf ("%lld\n ", u);
  }
  fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
  return 0;
}

B. Imbalanced Arrays

https://codeforces.com/contest/1852/problem/B

image

image

考虑构造方案,显然 \(a_i\) 大的最后的 \(b_i\) 也会大,所以先以 \(a_i\) 为关键字从大到小排序,考虑枚举一个 \(k(0 \leq k \leq n)\),有 \(b_1 \sim b_k\) 是正的,\(b_{k + 1} \sim b_n\) 是负的,并且 \(b_1 \geq b_2 \geq \dots \geq b_n\);比 \(b_i(1 \leq i \leq k)\) 应该填 \(a_i - i + 1\),不大于 \(i\) 绝对值的(相加必定大于 \(0\),因为 \(w\)\(-w\) 不同时出现)有 \(a_i - i + 1\),大于 \(i\) 的绝对值的并且加和为正数,只有可能是正数,有 \(i - 1\) 个,所以 \(a_i + a_j > 0\)\(j\) 的数量为 \(a_i - i + 1 + i - 1\)\(a_i\) 个。

\(b_{k + 1} \sim b_n\) 就可以倒着从大到小填。

最后考虑可能有解的 \(k\) 的个数,有 \(k\) 个正数,那么 \(a_1, a_2, \dots, a_k\) 一定至少为 \(k\),因为有单调性,所以只需满足 \(a_k \geq k\);大的加小的为正数的方案数应该与小的加大的为正数的方案数相等,所以 \(\displaystyle \sum_{i = 1}^{k}a_i - k^2 = \sum_{i = k+1}^{n}a_i\),可以证明(但是我不会 lol)满足条件的 \(k\) 最多只有 \(1\) 个。

最后检验一下就行了,如果实在满足不了就是无解。

/**
  * author : OMG_78
  * created: 2023-07-24-09.58.56
 **/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
  if (c < '0') return true;
  if (c > '9') return true;
  return false;
}
template < class Z >
inline void read (Z &tmp) {
  Z x = 0, f = 0;
  char c = getchar ();
  for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
  for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
  tmp = !f ? x : -x;
}
struct node {
  int id, w;
} qwq[100005];
ll sm[100005], qws[100005];
inline bool cmp (node p1, node p2) {
  return p1.w > p2.w;
}
int n, ans[100005];
inline bool solveq (int idx) {
  set < int > aq; aq.clear ();
  for (int i = 1;i <= n; ++ i) aq.insert (i);
  for (int i = 1;i <= idx; ++ i) {
    ans[i] = qwq[i].w - i + 1;
    if (ans[i] <= 0) {
      return false;
    }
    aq.erase (ans[i]);
  }
  for (int i = n;i > idx; -- i) {
    ans[i] = *aq.rbegin ();
    aq.erase (ans[i]);
    ans[i] = -ans[i];
  }
  for (int i = 1;i <= n; ++ i) {
    if (qwq[i].w && ans[qwq[i].w] + ans[i] <= 0) return false;
    if (qwq[i].w < n && ans[qwq[i].w + 1] + ans[i] >= 0) return false;
  }
  printf ("YES\n");
  for (int i = 1;i <= n; ++ i) qws[qwq[i].id] = ans[i];
  for (int i = 1;i <= n; ++ i) printf ("%d ", qws[i]);
  printf ("\n");
  return true;
}
inline void solve () {
  read (n);
  for (int i = 1;i <= n; ++ i) read (qwq[i].w), qwq[i].id = i;
  sort (qwq + 1, qwq + 1 + n, cmp);
  for (int i = 1;i <= n; ++ i) sm[i] = sm[i - 1] + 1ll * qwq[i].w;
  int idx = -1;
  for (int k = 0;k <= n; ++ k) {
    if (qwq[k].w >= k && sm[k] - (1ll * k) * (1ll * k) == sm[n] - sm[k]) {
      if (solveq (k)) return ;
    }
  }
  printf ("NO\n");
}
bool Memory_Ends;
signed main () {
  fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
  int _; read (_);
  while (_ --) solve ();
  fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
  return 0;
}

C. Ina of the Mountain

https://codeforces.com/contest/1852/problem/C

image

image

如果没有 \(\bmod k\) 的限制,发现就是差分数组的正项求和,即 \(\displaystyle \sum_{i = 1}^{n}\max(0, a_i - a_{i - 1})\)

可以将 \(\bmod k\) 转化为将每个数事先加上若干个 \(k\),然后就不用再考虑 \(\bmod k\),直接算即可。

考虑贪心地加 \(k\)

  • 首先 \(a_i\)\(a_{i - 1}\) 加同样数量的 \(k\)

  • 如果 \(a_{i} \leq a_{i - 1}\),贡献为 \(0\),那最好,不要再动了。

  • 如果 \(a_{i} > a_{i - 1}\),分两种情况:躺平,\(a_{i} - a_{i - 1}\) 的代价就可以了;给 \([j, i) (1 \leq j < i)\) 的所有 \(a_x\) 都加上一个 \(k\),对差分数组的贡献就是 \(a_{j} + k - a_{j - 1}\)。第二种情况考虑 反悔贪心

首先用一个堆存每一个点加上 \(k\) 对差分数组的贡献,\(a_{i} \leq a_{i - 1}\) 的贡献就是 \(a_{i} + k - a_{i - 1}\),没啥好说的;\(a_{i} > a_{i - 1}\) 如果 \(a_{i} - a_{i - 1}\) 是最优的就直接加上,没啥好说的,否则就加上堆中最小的,就是反悔的过程,然后堆中加上 \(a_{i} - a_{i - 1}\),方便以后反悔。

/**
  * author : OMG_78
  * created: 2023-07-25-09.39.18
 **/
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline bool ok (char c) {
  if (c < '0') return true;
  if (c > '9') return true;
  return false;
}
template < class Z >
inline void read (Z &tmp) {
  Z x = 0, f = 0;
  char c = getchar ();
  for ( ; ok (c) ; c = getchar ()) f = (c == '-') ? 1 : f;
  for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
  tmp = !f ? x : -x;
}
int n, k, a, prevq;
#define prev prevq
ll ans;
priority_queue < int, vector < int >, greater < int > > heap;
bool Memory_Ends;
signed main () {
  fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
  int _; read (_);
  while (_ --) {
    read (n), read (k);
    prev = 0;
    while (!heap.empty ()) heap.pop ();
    ans = 0;
    for (int i = 1;i <= n; ++ i) {
      read (a);
      a %= k;
      if (prev > a) {
        heap.push (a + k - prev);
      }
      else {
        heap.push (a - prev);
        ans += 1ll * heap.top ();
        heap.pop ();
      }
      prev = a;
    }
    printf ("%lld\n", ans);
  }
  fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
  return 0;
}
posted @ 2023-07-24 17:23  CountingGroup  阅读(82)  评论(0编辑  收藏  举报