AtCoder Beginner Contest 203(Sponsored by Panasonic)

比赛链接:https://atcoder.jp/contests/abc203/tasks

A - Chinchirorin

题意

给出三个数,如果有两个数相同,输出剩下的那个数。

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int a, b, c;
    cin >> a >> b >> c;
    if (a == b or b == c or a == c) {
        cout << (a ^ b ^ c) << "\n";
    } else {
        cout << 0 << "\n";
    }
    return 0;
}

B - AtCoder Condominium

题意

一栋公寓有 \(n\) 层,每层有 \(k\) 个房间,第 \(i\) 层第 \(j\) 间房间号为 \(i0j\) ,计算所有房间号之和。

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            ans += i * 100 + j;
        }
    }
    cout << ans << "\n";
    return 0;
}

C - Friends and Travel costs

题意

有无穷多个村庄,开始时 Taro 在村庄 \(0\) ,他可以花费 \(1\) 元从村庄 \(i\) 移动到村庄 \(i + 1\)

Taro 有 \(n\) 个朋友,给出他们所在的村庄 \(a_i\) 和 Taro 到达他们村庄时他们给 Taro 的钱数 \(b_i\) ,计算 Taro 最远可以到达的村庄。

题解

模拟。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    long long n, k;
    cin >> n >> k;
    vector<pair<long long, int>> v(n);
    for (int i = 0; i < n; i++) {
        cin >> v[i].first >> v[i].second;
    }
    sort(v.begin(), v.end());
    for (auto [x, y] : v) {
        if (k >= x) {
            k += y;
        } else {
            break;
        }
    }
    cout << k << "\n";
    return 0;
}

D - Pond

题意

给出一个 \(n\) 阶方阵,计算所有 \(k\) 阶方阵的中位数的最小值。

这里 \(k\) 阶方阵的中位数为: \(k^2\) 个数中第 \(\lfloor \frac{k^2}{2} \rfloor + 1\) 个大的数。

题解

二分中位数的值,将大于二分值的数记为 \(1\) ,小于等于的记为 \(0\) ,作二维前缀和来枚举所有 \(k\) 阶方阵。

如果存在一个方阵满足其和小于等于 \(\lfloor \frac{k^2}{2} \rfloor\) ,则当前二分值可行,设为上界,否则设为下界。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    vector<vector<int>> a(n + 1, vector<int> (n + 1));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> a[i][j];
        }
    }
    int l = 0, r = 1e9, ans = 0;
    while (l <= r) {
        int mid = (l + r) / 2;
        vector<vector<int>> pref_sum(n + 1, vector<int> (n + 1));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                pref_sum[i][j] = pref_sum[i - 1][j] + pref_sum[i][j - 1] - pref_sum[i - 1][j - 1] + (a[i - 1][j - 1] > mid);
            }
        }
        bool possible = false;
        for (int i = k; i <= n; i++) {
            for (int j = k; j <= n; j++) {
                if (pref_sum[i][j] - pref_sum[i - k][j] - pref_sum[i][j - k] + pref_sum[i - k][j - k] <= k * k / 2) {
                    possible = true;
                }
            }
        }
        if (possible) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    cout << ans << "\n";
    return 0;
}

E - White Pawn

题意

给出 \((2n + 1) \times (2n + 1)\) 的棋盘上 \(m\) 枚黑子的坐标,开始时一枚白子在 \((0, n)\) 处,白子的移动策略如下:

  • \((i + 1, j)\) 没有黑子,则可以移动到 \((i + 1, j)\)
  • \((i + 1, j - 1)\) 有黑子,则可以移动到 \((i + 1, j - 1)\)
  • \((i + 1, j + 1)\) 有黑子,则可以移动到 \((i + 1, j + 1)\)

问白子可能到达第 \(2n\) 行的哪些坐标。

题解

如果遇到空行白子只能往下移,即空行不影响纵坐标的变化。

所以从上往下依次枚举有黑子的行即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    map<int, vector<int>> mp;
    for (int i = 0; i < m; i++) {
        int x, y;
        cin >> x >> y;
        mp[x].push_back(y);
    }
    set<int> prev_row{n};
    for (auto [x, vec_y] : mp) {
        vector<int> next_row;
        for (auto y : vec_y) {
            for (auto d : {-1, 1}) {
                if (prev_row.count(y + d)) {
                    next_row.push_back(y);
                }
            }
        }
        for (auto y : vec_y) {
            prev_row.erase(y);
        }
        for (auto y : next_row) {
            prev_row.insert(y);
        }
    }
    cout << prev_row.size() << "\n";
    return 0;
}

F - Weed

题意

花园里有 \(n\) 株野草,Takahashi 和 Aoki 打算按如下方案拔掉这些野草:

  • Aoki 先拔掉最多 \(k\) 株野草
  • 之后 Takahashi 重复如下操作:
    • 假设 \(h\) 为余下野草中的最大高度,拔掉所有高度大于 \(\lfloor \frac{h}{2} \rfloor\) 的野草

问在 Takahashi 操作最少次的情况下, Aoki 最少要拔掉多少株野草。

题解

\(dp_{ij}\) 为前 \(i\) 株野草 Takahashi 操作 \(j\) 次时 Aoki 要拔掉多少株。

  • 若第 \(i\) 株由 Aoki 拔掉,那么有状态转移方程:

    • \(dp_{ij} = min(dp_{ij}, dp_{i - 1j} + 1)\)
  • 若第 \(i\) 株由 Takahashi 拔掉,由于其操作的特殊性,假设此时还剩 \(x\) 株,那么有状态转移方程:

    • \(dp_{ij+1} = min(dp_{ij+1}, dp_{xj})\) ,即 Aoki 无需再对 \([x + 1, i]\) 间的野草进行任何操作。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(a.begin(), a.end());
    const int m = 32;
    const int INF = n;
    vector<vector<int>> dp(n + 1, vector<int> (m, INF));
    dp[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1);
        }
        int x = upper_bound(a.begin(), a.end(), a[i - 1] / 2) - a.begin();
        for (int j = 0; j + 1 < m; j++) {
            dp[i][j + 1] = min(dp[i][j + 1], dp[x][j]);
        }
    }
    for (int i = 0; i < m; i++) {
        if (dp[n][i] > k) {
            continue;
        }
        cout << i << ' ' << dp[n][i] << "\n";
        break;
    }
    return 0;
}
posted @ 2021-06-01 18:01  Kanoon  阅读(230)  评论(0编辑  收藏  举报