Codeforces Round #844 (Div. 1 + Div. 2, based on VK Cup 2022 - Elimination Round) A-D

空白菌發表於2023-01-17

比賽連結

A

題意

設計一條線路要貼著6個牆面走,從 \((a,b)\)\((f,g)\) ,線路長度最短。

題解

知識點:模擬。

分類取最短即可。

時間複雜度 \(O(1)\)

空間複雜度 \(O(1)\)

程式碼

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool solve() {
    int w, d, h;
    int a, b, f, g;
    cin >> w >> d >> h;
    cin >> a >> b >> f >> g;
    int ans = h + min(abs(a - f) + min(b + g, 2 * d - b - g), abs(b - g) + min(a + f, 2 * w - a - f));
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

B

題意

\(n\) 個人要去電影院,第 \(i\) 個人要求至少 \(a_i\) 個其他人去他才去,問最終能有多少種選人去電影院的方案,保證每種方案選完後所有符合要求的都去了。

題解

知識點:貪心。

從小到大排序。如果低要求的人能去就先去,保證要求高的人去之前能去的都去。如果低要求的都去不了,換成高要求的更去不了,不如先預選低要求的,看看後面能不能補上。如此,可以得到所有方案。

因為可以都不去,所以如果第一個人就有 \(\geq 1\) 的要求時,顯然是可以都不去的。

對於 \(i\in[1,n)\) ,如果 \(a_i \leq i-1\)\(a_{i+1} > i\) ,說明 \([1,i]\) 都能去,但不能直接選 \(i+1\) ,因為缺人需要繼續安排後面的看看能不能補上,所以這裡可以方案加一。

其他情況,\([1,i+1]\) 都能去則必須安排在一個方案;\([1,i+1]\) 都不能去,繼續選,不能算做一個方案;\([1,i]\) 不能去,但 \([1,i+1]\) 可以去,此時要繼續往後選,讓這個方案能去的人都去。

最後,上述判斷包括不了全都去,因此特判方案加一。

時間複雜度 \(O(n \log n)\)

空間複雜度 \(O(n)\)

程式碼

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int a[200007];
bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    int ans = 0;
    if (a[1] != 0) ans++;
    for (int i = 1;i < n;i++) {
        if (a[i] <= i - 1 && a[i + 1] > i) ans++;
    }
    ans++;
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

C

題意

給出一個小寫字母的字串,每次修改可以使一個位置的字母替換成任意字母,求出修改的最少次數,使字串的字母出現次數相同,並輸出修改後的字串。

題解

知識點:模擬,列舉,貪心。

注意到,直接列舉最後的個數很困難,但列舉最後有幾種字母很容易,因此考慮列舉最後剩多少種字母。

接下來分兩步走:

  1. 確定最少要變多少位置,同時確定最少答案情況的字母種數。
  2. 透過上一步確定的資訊,遍歷字串更改。

第一步:

先將每個字母對應的出現次數記錄好,同時儲存字母本身的序號,方便排序後還能找到對應的字母。

列舉字母種數 \(i\) ,滿足 \(i \mid n\) ,則最終每種字母會有 \(x = \dfrac{n}{i}\) 個。我們可以貪心地選擇保留數量最多的前 \(i\) 個,這些字母中數量大於 \(x\) 是必須修改的,而後 \(26-i\) 個字母,全部都需要修改。於是,就可以求出 \(i\) 對應的修改次數 \(delta\) ,列舉取最小值,並記錄最終種數 $div $ 和每種數量 \(cnt\),即可。

第二步:

我們此時需要遍歷字串修改,因此需要透過字母序號得到字母的排名(從 \(0\) 開始)和數量,所以需要遍歷第一步得到的排名對應數量和序號的陣列獲得。

若某個位置的字母的數量大於 \(cnt\) 或者排名大於等於 \(div\) 並且字母的數量大於 \(0\) 則需要修改,列舉 \(26\) 個字母找到排名小於 \(div\) 且數量小於 \(cnt\) 的填充進去即可。

時間複雜度 \(O(n)\)

空間複雜度 \(O(n)\)

程式碼

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    vector<pair<int, int>> rk(26);
    for (int i = 0;i < 26;i++) rk[i] = { 0,i };
    for (int i = 0;i < n;i++) rk[s[i] - 'a'].first++;
    sort(rk.begin(), rk.end(), greater<pair<int, int>>());
    int div = 1;
    int ans = 1e9;
    for (int i = 1;i <= 26;i++) {
        if (n % i) continue;
        int x = n / i;
        int delta = 0;
        for (int j = 0;j < i;j++) delta += max(0, rk[j].first - x);
        for (int j = i;j < 26;j++) delta += rk[j].first;
        if (delta < ans) {
            ans = delta;
            div = i;
        }
    }
    int cnt = n / div;
    vector<pair<int, int>> pos(26);
    for (int i = 0;i < 26;i++) pos[rk[i].second] = { rk[i].first,i };
    for (int i = 0;i < n;i++) {
        if (pos[s[i] - 'a'].first > cnt || pos[s[i] - 'a'].second >= div && pos[s[i] - 'a'].first) {
            pos[s[i] - 'a'].first--;
            for (int j = 0;j < 26;j++) {
                if (pos[j].first < cnt && pos[j].second < div) {
                    s[i] = j + 'a';
                    pos[j].first++;
                    break;
                }
            }
        }
    }
    cout << ans << '\n';
    cout << s << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

D

題意

給出一個陣列 \(a_i \in [1,10^9]\) ,求出 \(x \in [0, 10^{18}]\)\(a_1+x,\cdots,a_n+x\) 中完全平方數的數量的最大值。

題解

知識點:列舉,因數集合。

顯然,必然存在 \(x\) 使得 \(a+x\) 是個完全平方數,答案至少為 \(1\) 。考慮答案為 \(2\) 及以上的情況。

我們可以先列舉所有兩個數的組合 \(a_i,a_j(i<j)\) ,如果存在大於等於 \(2\) 的答案,必然會包括這些兩個數的組合,因而我們可以透過兩個數列舉出所有成立的 \(x\) ,對每個 \(x\) 在完整的陣列中再跑一遍記錄答案即可。

我們考慮如何得到使 \(a_i+x,a_j+x\) 都成為完全平方數的 \(x\) 。設 \(a_i+x = s^2,a_j+x = t^2\) ,直接列舉 \(x\) 複雜度是 \(10^9\) ,考慮列舉 \(s,t\) 相關的數。我們可以得到 \(a_j-a_i = t^2-s^2 = (t+s)(t-s) \in [1,10^9)\) , 因此我們可以列舉 \(t+s,t-s\) ,即列舉 \(a_j-a_i\) 的因子即可,我們最多隻需要列舉 \(\sqrt {10^9}\) 次即可,然後再求出 \(t,s\) ,就可以得到 \(x\) 了。

時間複雜度 \(O(n^2\sqrt{10^9})\)

空間複雜度 \(O(n)\)

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool issqr(ll n) {
    ll x = sqrt(n);
    return x * x == n;
}

int a[57];
bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> a[i];
    int ans = 1;
    for (int i = 1;i <= n;i++) {
        for (int j = i + 1;j <= n;j++) {
            int d = a[j] - a[i];
            ll x = -1;
            for (int k = 1;k * k <= d;k++) {
                if (d % k || ((d / k + k) & 1)) continue;
                int s = (d / k + k) / 2;
                int t = (d / k - k) / 2;
                if (1LL * s * s < a[j] || 1LL * t * t < a[i]) continue;
                x = 1LL * s * s - a[j];
                int cnt = 0;
                for (int k = 1;k <= n;k++) if (issqr(a[k] + x)) cnt++;
                ans = max(ans, cnt);
            }
        }
    }
    cout << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

相關文章