VP Codeforces Round 876 (Div. 2)
A. The Good Array
题意:一个长度为\(n\)的数组,满足每个\(i\)都有前\(i\)个数和后\(i\)个数有\(\lceil \frac{i}{k} \rceil\)个\(1\)。求这样的数组里\(1\)最少的有几个\(1\)。
从前往后模拟一遍,从后往前模拟一遍,记录\(1\)的个数就行。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0, s = 0; i < n; ++ i) {
if ((i + 1 + k - 1) / k > s) {
a[i] = 1;
++ s;
}
}
for (int i = n - 1, s = 0; i >= 0; -- i) {
s += a[i];
if ((n - i + k - 1) / k > s) {
a[i] = 1;
++ s;
}
}
std::cout << std::ranges::count(a, 1) << "\n";
}
B. Lamps
题意:\(n\)盏灯,每盏灯有\(a_i, b_i\)两个属性。如果你点亮第\(i\)盏灯,就获得\(b_i\)的收益。如果当前开启了\(k\)盏灯,那么所有\(a_i \leq k\)的灯都会损坏(包括已经亮的灯),已经亮的灯就不算点亮了。你最多对一个灯进行一次操作,求最大收益。
按\(a_i\)从小到大点,发现\(a_i\)小的一定先损坏,也就是不会影响比它大的灯损不损坏。那么按\(a_i\)分类,对于\(a_i\)类灯,取前\(a_i\)大的灯就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<int>> a(n + 1);
for (int i = 0; i < n; ++ i) {
int x, y;
std::cin >> x >> y;
a[x].push_back(y);
}
i64 ans = 0;
for (int i = 1; i <= n; ++ i) {
std::ranges::sort(a[i], std::greater<>());
for (int j = 0; j < i && j < a[i].size(); ++ j) {
ans += a[i][j];
}
}
std::cout << ans << "\n";
}
C. Insert Zero and Invert Prefix
题意:你需要\(n\)操作构造一个\(b\)数组:一开始为空,每次在一个位置插入一个\(0\),这个\(0\)前面的数都会取反。求变成\(a\)数组的方案。
观察规律。
从后往前做,取每一段为\(1111..110000..00\)的段,也就是前面若干个\(1\)后面若干个\(0\)。对于这样的段,先放\(cnt_0 + cnt_1 - 1\)个\(0\),然后再在第\(cnt_1\)个位置放\(0\),那么就可以变成这个样子。然后往前继续做,这段就会被顶到后面去,最后刚好对齐。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::ranges::reverse(a);
if (a[0] == 1) {
std::cout << "NO\n";
return;
}
std::vector<int> ans;
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && a[j + 1] == 0) {
++ j;
}
int cnt0 = j - i + 1;
while (j + 1 < n && a[j + 1] == 1) {
++ j;
}
int cnt1 = j - i + 1 - cnt0;
while (cnt0 > 1) {
ans.push_back(0);
-- cnt0;
}
for (int k = 0; k < cnt1; ++ k) {
ans.push_back(0);
}
ans.push_back(cnt1);
i = j;
}
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
D. Ball Sorting
题意:给你一个排列,你想要把它变成升序。给你\(k\)个\(0\),你可以把它们插入到任意位置,然后每个挨着\(0\)的数可以移动到任意位置。求当\(k = {1, 2, ..., n}\)的时候,最少操作数是多少。
显然每个数只会操作一次。
那么我们希望不操作的数最多,它们肯定组成一个上升子序列,因为它们不动,所以顺序不会变,要想最后有序,那么它们组成的子序列也是有序的。那么我们需要在这个子序列两两之间放\(0\)。
记\(f[i][j]\)为有\(i\)个\(0\),上升子序列目前结尾在\(j\)的最少操作数,那么\(f[i][j] = [a_k < a_j]\min f[i - 1][k] + j - k - 1\)。可以放两个哨兵:\(a_0 = 0, a_{n+1} = inf\)。那么\(f[i][n + 1]\)就是\(k=i\)的答案。细节就是\(a_{j-1} < a_{j}\)那么\(f[i][j] = \min(f[i][j], f[i][j - 1])\)。还有\(f[i][j] = \min(f[i][j], f[i - 1][j])\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 2);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
a[n + 1] = 1e9;
const int inf = 1e9;
std::vector f(n + 1, std::vector<int>(n + 2, inf));
f[0][0] = 0;
for (int i = 1; i <= n + 1; ++ i) {
if (a[i] > a[i - 1]) {
f[0][i] = 0;
} else {
break;
}
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n + 1; ++ j) {
f[i][j] = f[i - 1][j];
if (a[j - 1] < a[j]) {
f[i][j] = std::min(f[i][j], f[i][j - 1]);
}
for (int k = 0; k < j; ++ k) {
if (a[k] < a[j]) {
f[i][j] = std::min(f[i][j], f[i - 1][k] + j - k - 1);
}
}
}
}
for (int i = 1; i <= n; ++ i) {
std::cout << f[i][n + 1] << " \n"[i == n];
}
}

浙公网安备 33010602011771号