SMU Summer 2024 Contest Round 2

Ke_scholar發表於2024-07-09

SMU Summer 2024 Contest Round 2

Sierpinski carpet

題意

給一個整數 n ,輸出對應的 \(3^n\times 3^n\) 的矩陣。

思路

\(n = 0\) 時是 # ,之後每級矩陣都是中間 \(3^{n-1}\times 3^{n-1}\) 矩陣為全點,周圍八個矩陣為上一級的圖案,按題意模擬即可。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    map<int, vector<string>> mp;
    mp[0] = {"#"};

    auto ok = [&](vector<string> s, int m)->vector<string> {
        const int sn = s.size();
        int N = 3;
        for (int i = 1; i < m; i ++) {
            N *= 3;
        }

        vector<string> res(N);
        for (int i = 0; i < N ; i ++) {
            string cs;
            if (i >= N / 3 && i < N / 3 * 2) {
                cs += s[i % sn] + string(sn, '.') + s[i % sn];
            } else {
                cs += s[i % sn] + s[i % sn] + s[i % sn];
            }
            res[i] = cs;
        }

        return res;
    };

    for (int i = 1; i <= n; i ++) {
        mp[i] = ok(mp[i - 1], i);
    }

    for (auto &i : mp[n])
        cout << i << '\n';

    return 0;
}

Consecutive

題意

給一個字串,\(Q\) 次詢問 \([l,r]\) 區間內有多少對相鄰且相同的字母。

思路

字首和處理,注意邊界需要特判一下。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, q;
    cin >> n >> q;

    string s;
    cin >> s;

    s = " " + s;
    vector<int> pre(n + 1);
    for (int i = 1; i <= n; i ++) {
        pre[i] = pre[i - 1];
        if (s[i] == s[i + 1]) pre[i] ++;
    }

    while (q--) {
        int l, r;
        cin >> l >> r;
        cout << pre[r] - pre[l - 1] - (r < n && s[r] == s[r + 1]) << '\n';
    }

    return 0;
}

Minimum Width

題意

給你 n 個數,每個數代表一個單詞的長度,單詞之間挨著的間距為 1 ,行首間距不算,現要求你設計一行的長度 w ,使得這些單詞最多排列 m 行,問 w 最小是多少。

思路

w 越大,排列行數一定越小,所以答案滿足單調性,可以二分答案,需要注意的是最小邊界應該是單詞中長度最大的那個,或者在 check 的時候特判一下。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<i64> L(n + 2);
    for (int i = 1; i <= n; i ++)
        cin >> L[i];
    L[n + 1] = LLONG_MAX / 2;

    auto check = [&](i64 x) -> bool{
        i64 res = 0, now = 0;
        for (int i = 1; i <= n; i ++) {
            if (x < L[i]) return false;
            now += L[i];
            if (now + 1 + L[i + 1] > x) {
                now = 0;
                res ++;
            } else {
                now ++;
            }
            if (res > m) return false;
        }
        return res <= m;
    };

    i64 l = 0, r = 10000000000000000ll, ans = 1;

    while (l <= r) {
        i64 mid = l + r >> 1;
        if (check(mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }

    cout << ans << '\n';

    return 0;
}

Printing Machine

題意

給你 n 個景點的開門時間和持續時間,你想到這些景點去打卡,只要在開門時間或者關門時的那瞬間打卡都可以,但是每打卡一次你需要休息 1 單位時間,問你最多可以打卡多少個景點。

思路

考慮貪心。

要使得打卡景點最多,首先應該考慮開門最早並且持續時間短的景點,所以可以將這些區間先排序,且資料範圍給到了 1e18,所以不能去列舉單位時間,對於在當前時間開門的所有景點,我們可以把它的關門時間丟進優先佇列裡,優先去最早關門的店,如果有景點的關門時間比當前時間更早,說明我們無法去這個景點,彈出佇列即可,如果當前時間沒有景點開門,我們直接跳到最近一個景點的開門時間即可。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

using PII = pair<i64, i64>;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    vector<PII> td(n);
    for (auto &[t, d] : td) {
        cin >> t >> d;
        d += t;
    }

    sort(td.begin(), td.end());

    priority_queue<i64, vector<i64>, greater<>> Q;
    i64 time = 1, ans = 0, pos = 0;

    while (true) {
        if (Q.empty()) {
            if (pos == n) break;
            time = td[pos].first;
            Q.push(td[pos++].second);
        }
        while (pos < n && td[pos].first == time)
            Q.push(td[pos++].second);
        while (Q.size() && Q.top() < time)
            Q.pop();
        if (Q.size()) ans ++, Q.pop();
        time ++;
    }

    cout << ans << '\n';

    return 0;
}

Nearest Black Vertex

題意

給你 n 個點,m 條邊的無向連通圖,你可以將這些點染成黑白兩色,現給你 k 個條件,要求使得對於第 \(P_i\) 個點,它與離他最近的黑色的點距離為 \(D_i\),問是否有染色方案可以滿足這些條件,有就輸出 \(Yes\) 和對應方案,否則輸出 \(No\)

思路

首先可以用 BFS 先計算出每個點到其他點的距離,其次先預設全部點都是黑色,然後去遍歷 k 個條件,將與 \(P_i\) 點距離小於 \(D_i\) 的點都染成白色,處理出最終黑色的點,然後再去遍歷一次條件,判斷所有黑點與 \(P_i\) 的最小距離是否為 \(D_i\) 即可。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector g(n + 1, vector<int>());
    for (int i = 0; i < m; i ++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    vector dis(n + 1, vector<int>(n + 1));
    auto bfs = [&](int s) {
        vector<bool> vis(n + 1);
        queue<array<int, 2>> Q;
        Q.push({s, 0});

        while (Q.size()) {
            auto [u, len] = Q.front();
            Q.pop();

            if (vis[u]) continue;
            vis[u] = 1;

            dis[s][u] = len;
            for (auto &v : g[u]) {
                if (!vis[v] && v != u) {
                    Q.push({v, len + 1});
                }
            }
        }
    };

    for (int i = 1; i <= n; i ++)
        bfs(i);

    int k;
    cin >> k;

    vector<bool> col(n + 1, 1);
    vector<array<int, 2>> pd(k);
    for (auto &[p, d] : pd) {
        cin >> p >> d;
        for (int i = 1; i <= n; i ++)
            if (dis[p][i] < d)
                col[i] = 0;
    }

    for (auto &[p, d] : pd) {
        int t = 1 << 30;
        for (int i = 1; i <= n; i ++)
            if (col[i])
                t = min(t, dis[p][i]);
        if (t != d) {
            cout << "No\n";
            return 0;
        }
    }

    cout << "Yes\n";
    for (int i = 1; i <= n; i ++)
        cout << col[i];

    return 0;
}

Christmas Present 2

題意

給你一個起點和 n 個孩子的位置,你每次從家出發可以帶 k 個禮物,也可以隨時回家,現你需要從起點出發按順序把禮物發個 n 個孩子,送完禮物後最終回到家,問你這個送禮物的最短路程。

思路

因為按順序發禮物,所以當你處在第 \(i\) 個位置時,就是在 \(i-k\)\(i-1\) 的位置中選擇一處回家,考慮dp。

\(dp_i\) 為從起點到第 \(i\) 個孩子的最短路程,假設在 \(j\) 處回家,那麼轉移方程為:

\[dp_i=Min_{i-k}^{i-1}dp_j+home_j+home_{j+1}+dis_{(j+1,i)} \]

image

對於 \(dis_{(j+1,i)}\) 可以用字首和處理成 \(Pre_i - Pre_{j+1}\),那麼可化為:

\[dp_i=Min_{i-k}^{i-1}dp_j+home_j+home_{j+1}+Pre_i-Pre_{j+1}\\ =Pre_i+Min_{i-k}^{i-1}dp_j+home_j+home_{j+1}-Pre_{j+1} \]

對於後面的 \(Min\) 的一串可以使用單調佇列 \(O(n)\) 最佳化。

注意單調佇列開始應該放入一個 0,表示給前面的 k 個孩子發禮物後可以不回家。

程式碼

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, k;
    cin >> n >> k;

    vector<array<double, 2>> loc(n + 1);
    for (auto &[x, y] : loc)
        cin >> x >> y;

    vector<double> home(n + 1), pre(n + 1), dp(n + 1);
    for (int i = 1; i <= n; i ++) {
        home[i] = hypot(loc[i][0] - loc[0][0], loc[i][1] - loc[0][1]);
        pre[i] += pre[i - 1];
        pre[i] += hypot(loc[i][0] - loc[i - 1][0], loc[i][1] - loc[i - 1][1]);
    }

    auto calc = [&](int j)->double{
        if (!j) return 0;
        return dp[j] + home[j] + home[j + 1] - pre[j + 1];
    };

    deque<int> Q;
    Q.push_back(0);
    for (int i = 1; i <= n; i ++) {
        dp[i] = pre[i] + calc(Q.front());

        while (Q.size() && Q.front() <= i - k)
            Q.pop_front();

        while (Q.size() && calc(Q.back()) >= calc(i))
            Q.pop_back();

        Q.push_back(i);
    }

    printf("%.15lf", dp[n] + home[n]);

    return 0;
}

相關文章