AtCoder Beginner Contest 363

~Lanly~發表於2024-07-27

上週去玩了(逃

A - Piling Up (abc363 A)

題目大意

給定分數,問晉級還差多少分。分別到\(100,200,300\)分能晉級。

解題思路

找到第一個大於當前分數的,其差即為答案。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int a;
    cin >> a;
    vector<int> rk{100, 200, 300};
    auto it = upper_bound(rk.begin(), rk.end(), a);
    int ans = *it - a;
    cout << ans << '\n';

    return 0;
}



B - Japanese Cursed Doll (abc363 B)

題目大意

給定\(n\)個人的頭髮長度,每天會漲\(1\)釐米。問至少經過多少天,有至少\(P\)個人的頭髮長度至少是\(T\)

解題思路

由於頭髮都是一起漲的,將頭髮長度從長到短排序,當第\(P\)個人的頭髮漲到\(T\)時,前\(P-1\)個也至少是\(T\)了。所以答案就是第\(P\)個人的頭髮漲到\(T\)的時間。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, t, p;
    cin >> n >> t >> p;
    vector<int> a(n);
    for (auto& x : a) {
        cin >> x;
    }
    sort(a.begin(), a.end(), greater<int>());
    int ans = max(0, t - a[p - 1]);
    cout << ans << '\n';

    return 0;
}



C - Avoid K Palindrome 2 (abc363 C)

題目大意

給定一個字串,將字母排序,問有多少種情況,其不存在一個長度為\(k\)的迴文串。

解題思路

字串長度\(n\)只有\(10\),花\(O(10!)\)列舉所有的排列情況,然後花\(O(n)\)列舉子串,再花\(O(k)\)判斷是否是迴文串即可。時間複雜度是\(O(n!nk)\)

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, k;
    string s;
    cin >> n >> k >> s;
    int ans = 0;
    auto ispalindrome = [&](string&& s) -> bool {
        auto t = s;
        reverse(t.begin(), t.end());
        return t == s;
    };
    auto check = [&](string s) -> bool {
        for (int i = 0; i <= n - k; i++) {
            if (ispalindrome(s.substr(i, k))) {
                return false;
            }
        }
        return true;
    };
    sort(s.begin(), s.end());
    do {
        ans += check(s);
    } while (next_permutation(s.begin(), s.end()));
    cout << ans << '\n';

    return 0;
}



D - Palindromic Number (abc363 D)

題目大意

求第\(n\)小的迴文數字。

解題思路

觀察迴文數字,按長度分奇數和偶數兩種情況。

  • 1 2 3 4 5 6 7 8 9
  • 11 22 33 44 55 66 77 88 99
  • 101 111 121 131 141 151 ... 191
  • 202 212 222 232 242 252 ... 292
  • ...
  • 909 919 929 939 949 959 ... 999
  • 1001 1111 1221 1331 1441 ... 1991
  • 2002 2112 2222 2332 2442 ... 2992
  • ...
  • 9009 9119 9229 9339 9449 ... 9999

由於是前後是對稱的,由於數的比較是從高位比較,我們可以把右半部分的低位忽略,這樣就變成

  • 1 2 3 4 5 6 7 8 9
  • 1 2 3 4 5 6 7 8 9
  • 10 11 12 13 14 15 ... 19
  • 20 21 22 23 24 25 ... 29
  • ...
  • 90 91 92 93 94 95 ... 99
  • 10 11 12 13 14 15 ... 19
  • 20 21 22 23 24 25 ... 29
  • ...
  • 90 91 92 93 94 95 ... 99

長度是奇偶迴圈的,去除右半部分,容易發現它就是

  • 1 2 3 4 5 6 7 8 9
  • 10 11 12 13 14 15 16 17 18 19 ... 90 91 92 93 94 95 96 97 98 99
  • 100 101 .......

每行(即一個長度)多出現一次。而依次每個長度的數量分別是\(9,99,999,9999......\)

因此透過\(n\),得知道第\(n\)個數的長度,以及是該長度的奇還是偶。如何知道長度,列舉即可,其長度不會超過\(O(\log n)\)

知道該長度後,就知道該長度有多少個數字\(p\),再透過\(n \geq p\)來判斷是偶數情況還是奇數情況,\(n \% p\)就是對應的數了。這裡的\(n\)是減去小的長度後的值,即從\(1000\)(如果長度是\(4\))開始數的第幾個數。

程式碼中 s=to_string(n % p + p / 9),其中\(p/9\)是因為數是從\(1000\)開始的,而不是\(0000\)開始。

特判一下\(0\)

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    LL n;
    cin >> n;
    if (n == 1) {
        cout << "0\n";
        return 0;
    }
    n -= 2;
    LL p = 9;
    while (n >= 2 * p) {
        n -= 2 * p;
        p *= 10;
    }
    auto s = to_string(n % p + p / 9);
    auto t = s;
    reverse(t.begin(), t.end());
    if (n < p)
        s.pop_back();
    auto ans = s + t;
    cout << ans << '\n';

    return 0;
}



E - Sinking Land (abc363 E)

題目大意

方格島,四面環海。

給出島的高度,每年海平面上升\(1\)

\(y\)年的每一年,沒被淹的島的數量。

解題思路

起初是四周的島可能會被淹,稱之為危險島,我們需要比較危險島的高度和海平面高度。

因為越矮的島越容易被淹,我們優先比較危險島的高度最低的,如果被淹了,則會新增一些危險島,然後繼續比較高度最低的,直到沒被淹。

因此用優先佇列維護這些危險島的高度,當有新的危險島被淹時,新增周圍變成危險島的高度,模擬即可。

時間複雜度為\(O(hw\log hw)\)

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int h, w, y;
    cin >> h >> w >> y;
    vector<vector<int>> a(h, vector<int>(w));
    for (auto& i : a)
        for (auto& j : i)
            cin >> j;
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>,
                   greater<tuple<int, int, int>>>
        pq;
    vector<vector<int>> vis(h, vector<int>(w, 0));
    auto push = [&](int x, int y) {
        if (x >= 0 && x < h && y >= 0 && y < w && !vis[x][y]) {
            vis[x][y] = 1;
            pq.push({a[x][y], x, y});
        }
    };
    for (int i = 0; i < h; i++) {
        push(i, 0);
        push(i, w - 1);
    }
    for (int i = 0; i < w; i++) {
        push(0, i);
        push(h - 1, i);
    }
    array<int, 4> dx = {0, 0, 1, -1};
    array<int, 4> dy = {1, -1, 0, 0};
    int ans = h * w;
    for (int i = 1; i <= y; i++) {
        while (!pq.empty()) {
            auto [v, x, y] = pq.top();
            if (v <= i) {
                pq.pop();
                ans--;
                for (int j = 0; j < 4; j++) {
                    int nx = x + dx[j];
                    int ny = y + dy[j];
                    push(nx, ny);
                }
            } else {
                break;
            }
        }
        cout << ans << '\n';
    }

    return 0;
}



F - Palindromic Expression (abc363 F)

題目大意

給定\(n\),將\(n\)拆成若干個數相乘的表示式,其中這個表示式是個迴文串,且不存在數字\(0\)

解題思路

考慮樸素搜尋,每個表示式的每個乘數必定是\(n\)的因子,因此我們事先預處理出\(n\)的所有因子,分析可知我們只需預處理\(< \sqrt{n}\)的因子即可,大於\(\sqrt{n}\)的因子要麼其迴文數\(< \sqrt{n}\),要麼兩個數乘起來會\(> n\)了。

然後就列舉乘數是什麼,即列舉\(n\)的因子\(x\),然後求其迴文數字\(y\),判斷\(x\)\(xy\)是否都是\(n\)的因子。是的話,則就是\(n = x * \frac{n}{xy} * y\),接下來就是判斷\(\frac{n}{xy}\)能否表示成一個迴文表示式,這就是一個子問題了,記憶化搜尋一下即可。

考慮其複雜度,搜尋狀態的\(n\)的取值只有因數個,每次搜尋列舉因數,因此總的時間複雜度是\(O(\sigma^2(n))\),但實際會遠小於這個數。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    LL n;
    cin >> n;
    vector<int> fac;
    for (LL i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            fac.push_back(i);
        }
    }
    map<LL, pair<LL, LL>> dp;
    auto rev = [](LL x) {
        string s = to_string(x);
        reverse(s.begin(), s.end());
        return stoll(s);
    };
    auto is_palindromic = [&](LL x) { return x == rev(x); };
    auto is_valid = [&](LL x) {
        return to_string(x).find('0') == string::npos;
    };
    auto dfs = [&](auto dfs, LL n) -> bool {
        if (is_valid(n) && is_palindromic(n))
            return true;
        if (dp.count(n))
            return true;
        for (auto& x : fac) {
            auto y = rev(x);
            if (n % x == 0 && n / x % y == 0 && is_valid(x) && is_valid(y) &&
                dfs(dfs, n / x / y)) {
                dp[n] = {x, y};
                return true;
            }
        }
        return false;
    };
    dfs(dfs, n);
    if (dp.empty() && (!is_valid(n) || !is_palindromic(n)))
        cout << -1 << '\n';
    else {
        string ansl, ansr;
        while (dp.count(n)) {
            auto [x, y] = dp[n];
            ansr = ansr + "*" + to_string(x);
            ansl = to_string(y) + "*" + ansl;
            n /= x;
            n /= y;
        }
        string ans = ansl + to_string(n) + ansr;
        cout << ans << '\n';
    }

    return 0;
}



G - Dynamic Scheduling (abc363 G)

題目大意

給定\(n\)個任務的截止完成時間\(d_i\)和獎勵\(p_i\),指如果該任務能在\(d_i\)之前完成,則能獲得獎勵\(p_i\)

決定每天完成什麼任務,使得獎勵最大化。

維護\(q\)次操作,每次操作修改一個任務的\(d_i\)\(p_i\),修改後求上述答案,修改持久化。

解題思路

<++>

神奇的程式碼