Codeforces Round 876 (Div. 2) 题解 A - D
A. The Good Array
题目大意
给定两个整数 \(n\) 和 \(k\),规定一个\(01\)数列为好的的条件是,对于\(1\sim n\)中任意的 \(i\),都有:
- \(a\) 的前 \(i\) 个元素中至少有 \(\lceil\frac{i}k\rceil\) 个都是 \(1\)。
- \(a\) 的后 \(i\) 个元素中至少有 \(\lceil\frac{i}k\rceil\) 个都是 \(1\)。
问数列中 \(1\) 的数量的最小可能值。
解题思路
先考虑最简单的情况,\(k\) 取得 \(1\),这样我们的每一个元素都必须为 \(1\)。
如果 \(k\) 不是 \(1\),我们最终得到的序列一定是把 \(1\) 公摊使用的,那么前 \(k\) 个元素当中,只需要第一个元素是 \(1\) 即可,由前后缀的限制我们的数列也一定是对称的,依次类推,我们不难看出,实际上所求的数就是 \(\lceil\frac{n+k-1}{k}\rceil\) 个 \(1\)。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <cmath>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
void solve() {
double n, k;
cin >> n >> k;
if (k == 1) {
cout << n << endl;
return;
}
cout << ceil((n + k - 1) / k) << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
B. Lamps
题目大意
每个灯有打开,关闭和坏掉三种状态,以及两个参数 \(a\) 和 \(b\)。其中, \(b\) 代表打开一盏灯的得点。\(a\) 代表一种制约关系,当场上打开了 \(x\) 盏灯,那么场上所有 \(a\) 值小于 \(x\) 的灯会同时坏掉,不可以再次操作。
问最后最大得点为多少。
解题思路
首先我们来看看最优情况是怎么样的。
如果存在一个 \(a\) 值很小,比方说是 \(1\),然后 \(b\) 值很大的一盏灯,那么我们肯定会优先开启这盏灯,然后它会坏掉,同时保留这盏灯的得点。
那么我们可以对这些灯按照 \(a\) 值分类,在每一类中对 \(b\) 值由大到小排序,随后只需要暴力找每个值即可。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
void solve() {
int n;
LL res = 0;
cin >> n;
vector<vector<int>> v(n + 1);
for (int i = 1; i <= n; ++i) {
int x, y;
cin >> x >> y;
v[x].push_back(y);
}
for (int i = 1; i <= n; ++i) {
std::sort(v[i].begin(), v[i].end(), greater<>());
for (int j = 0; j < min(i, (int)v[i].size()); ++j) {
res += v[i][j];
}
}
cout << res << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
C. Insert Zero and Invert Prefix
题目大意
给定一个\(01\)数列 \(a\),同时初始化一个空\(01\)数列 \(b\),现在需要进行 \(n\) 次操作,每次操作可以选择 \(i \in [0, b.\text{length - 1}]\),将 \(b\) 中 \([0,i]\) 的元素取反,并在第\(i\) 位插入 \(0\),后面的元素不动。问能否操作构造出 \(a\) 数列。
解题思路
从零构造不好做的话我们可以考虑正难则反,考虑逆向情况。
题目转化为删去 \(a\) 中的某个 \(0\),然后将它前面的元素取反。
问能否通过恰好 \(n\) 次操作把 \(a\) 删干净。
我们可以双指针模拟这个过程,因为我们要取得最优情况一定是删去最靠前的 \(0\),这样才能保证每次操作更改的数最少。
需要注意的是我们操作后,数列元素个数有变化,所以他们的编号也有变化,需要使用位置变量来存一下。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
int a[N], res[N];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
int cnt = 0, pos = 1;
for (int i = 1; i <= n; ++i) {
if (!a[i]) {
res[++cnt] = i - pos;
for (int j = 1; j <= i - pos; ++j) {
res[++cnt] = 0;
}
pos = i + 1;
}
}
if (cnt != n) {
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
for (int i = n; i >= 1; --i) {
cout << res[i] << ' ';
}
cout << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
D. Ball Sorting
题目大意
有 \(n\) 个彩色球排成一列。这些球被涂成了 \(n\) 种不同的颜色,用数字从 \(1\) 到 \(n\) 表示。从左边数第 \(i\) 个球涂成颜色 \(c_i\)。现在需要重新排列这些球,使得从左边数第 \(i\) 个球的颜色为 \(i\) 。此外,你有 \(k\ge 1\) 个颜色为 \(0\) 的球,可以在重新排列过程中使用。
现在允许如下操作:
- 在序列的任意位置放置一个颜色为 \(0\) 的球,同时保持其他球的相对顺序。最多可以执行这个操作 \(k\) 次。
- 选择任意一个非零颜色的球同时至少有一个与它相邻的球颜色为 \(0\),将该非零颜色的球移到序列的任意位置,同时保持其他球的相对顺序不变。你可以无限次执行这个操作,但是每次操作你需要支付 \(1\) 个硬币。
操作结束之后颜色为零的球会消失,问排序完成的最小代价是多少。
解题思路
首先看本题的数据范围,最大只到 \(500\),那么 \(O(n^3)\) 的dp可解。
本题实际上是全排列的排序问题,我们颜色为 \(0\) 的球可以让他相邻两边的球归位,而当这个球走了,\(0\) 就会与新的球相邻,那么每个 \(0\) 实际上可以控制一段连续的序列。那么我们可以dp一个LIS(最长上升子序列),同时计算不处于LIS的部分被分成了几段即可。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 510;
const int MOD = 1e9 + 7;
int a[N];
int dp[N][N];
LL res[N];
void solve() {
memset(dp, -0x3f, sizeof dp);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
a[n + 1] = 0x3f3f3f3f;
dp[1][0] = 1;
for (int i = 2; i <= n; i++) {
dp[i][1] = 1;
}
for (int i = 2; i <= n + 1; i++) {
for (int k = 0; k <= i + 1; k++) {
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) {
if (j == i - 1) {
dp[i][k] = max(dp[i][k], dp[j][k] + 1);
} else if (k > 0) {
dp[i][k] = max(dp[i][k], dp[j][k - 1] + 1);
}
}
}
}
}
for (int k = 1; k <= n; ++k) {
dp[n + 1][k] = max(dp[n + 1][k], dp[n + 1][k - 1]);
cout << n + 1 - dp[n + 1][k] << ' ';
}
cout << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}