Codeforces Round 887 (Div. 1) 题解
https://codeforces.com/contest/1852/problems
A. Ntarsis' Set
https://codeforces.com/contest/1852/problem/A
感觉不是很一眼。
\(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
考虑构造方案,显然 \(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
如果没有 \(\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;
}