2024年夏休み交流戦・練習編1

2024年夏休み交流戦・練習編1

A - 🐓

AtCoder - abc079_d

問題文

各頂点のコストが与えられ、それを1に変換するのに必要な最小コストを求める。

解法

すべての数を1にするには、直接1に変換するか、別の数に変換してからさらに変換する方法がある。

これは$floyd$アルゴリズムによる最短経路探索と似ているため、$floyd$を適用できる。

コード

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

const int N = 2e2 + 10;
int cost[N][N], grid[N][N];

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

    int h, w;
    cin >> h >> w;

    for (int i = 0; i < 10; i ++)
        for (int j = 0; j < 10; j ++)
            cin >> cost[i][j];

    for (int i = 1; i <= h; i ++)
        for (int j = 1; j <= w; j ++)
            cin >> grid[i][j];

    for (int k = 0; k < 10; k ++)
        for (int i = 0; i < 10; i ++)
            for (int j = 0; j < 10; j ++)
                cost[i][j] = min(cost[i][j], cost[i][k] + cost[k][j]);

    i64 total = 0;
    for (int i = 1; i <= h; i ++)
        for (int j = 1; j <= w; j ++)
            total += cost[grid[i][j]][1];

    cout << total << '\n';

    return 0;
}

B - 🐓🐓

AtCoder - arc100_a

問題文

n個の数列が与えられる。任意の数bを選択し、|A₁-(b+1)|+...+|Aᵢ-(b+i)|+...+|Aₙ-(b+n)|を最小化する。

解法

一般項は|Aᵢ-(b+i)|である。

この式は|(Aᵢ-i)-b|と書き換えられる。

この値を小さくするには、bを(Aᵢ-i)に近づける必要がある。

したがって、Aᵢ-iを計算し、ソートした後、中央値をbとして選ぶことで最小化できる。

コード

#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;

    i64 result = 0;
    vector<i64> values(n + 1);
    for (int i = 1; i <= n; i ++) {
        cin >> values[i];
        values[i] -= i;
    }

    sort(values.begin() + 1, values.end());

    for (int i = 1; i <= n; i ++) {
        result += abs(values[i] - values[n / 2 + 1]);
    }

    cout << result << '\n';

    return 0;
}

C - 🐓🐓🐓

AtCoder - arc099_a

問題文

配列が与えられる。長さkの区間を一括で最小値に変更できる。配列全体が同じになるまで必要な操作回数を求める。

解法

kに関係なく、配列に1があれば、操作によりすべて1になる。

1のある位置からk個の区間を操作すると残り(n-k)個の要素が残る。

そのとき、1つだけ1を含み、残り(k-1)個は他の数を含む区間を操作することで、(k-1)個の数が1になる。

この操作回数は⌈(n-k)/(k-1)⌉であり、1回の操作を加えると⌈(n-1)/(k-1)⌉になる。

コード

#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;

    int position;
    vector<i64> arr(n + 1);
    for (int i = 1; i <= n; i ++) {
        cin >> arr[i];
    }

    int answer = (n - 1 + (k - 2)) / (k - 1);

    cout << answer << '\n';

    return 0;
}

D - 🐓🐓🐓🐓

AtCoder - arc100_b

問題文

数列Aを連続する4つの部分に分割し、それぞれの和の差を最小化する。

解法

累積和を計算した上で、1〜n-1から3つの位置i,j,kを選ぶと、sᵢ,sⱼ-sᵢ,sₖ-sⱼ,sₙ-sₖの差を最小化できる。

jを固定し、iとkはそれぞれ1〜j-1とj+1〜nの分割点でバランスを取るように選択する。

このとき、iとkはそれぞれsⱼ-sᵢとsₙ-sₖの差を最小化するように選ぶ。

コード

#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;

    vector<i64> nums(n + 1), prefix(n + 1);
    for (int i = 1; i <= n; i ++) {
        cin >> nums[i];
        prefix[i] = prefix[i - 1] + nums[i];
    }

    i64 minimum = 1 << 30;

    i64 max_val, min_val, start1=1, start2=3;
    for (int i = 2; i < n; i ++) {
        while(start1 < i && abs(prefix[start1]-(prefix[i]-prefix[start1]))>abs(prefix[start1+1]-(prefix[i]-prefix[start1+1])))
            start1 ++;
        while(start2<n &&abs((prefix[start2]-prefix[i])-(prefix[n]-prefix[start2]))>abs((prefix[start2+1]-prefix[i])-(prefix[n]-prefix[start2+1])))
            start2 ++;
        max_val = max({prefix[start1], prefix[i] - prefix[start1], prefix[start2] - prefix[i], prefix[n] - prefix[start2]});
        min_val = min({prefix[start1], prefix[i] - prefix[start1], prefix[start2] - prefix[i], prefix[n] - prefix[start2]});
        minimum = min(minimum, max_val - min_val);

    }

    cout << minimum << '\n';

    return 0;
}

E - 🐓🐓🐓🐓🐓

CodeForces - 1808C

問題文

区間内の任意の数を選び、その数の最大桁と最小桁の差を最小にする。

解法

数位DPを使用する。

lとrの桁数が異なる場合、999...999(n-1桁)が答えになる。

同じ場合、数位DPで処理する。

dp[長さ][最大][最小][上限][下限]で状態を管理し、再帰的に計算する。

コード

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

i64 dp[25][20][20][2][2];
i64 numbers[25][20][20][2][2];

void solve() {

    i64 left, right;
    cin >> left >> right;

    vector<int> upper(20, -1), lower(20, -1);
    lower[1] = left % 10;
    i64 len_left = 1, offset = 1, temp = left / 10, len_right = 1;
    while (temp) {
        offset *= 10;
        lower[++len_left] = temp % 10;
        temp /= 10;
    }

    temp = right / 10, upper[1] = right % 10;
    while (temp) {
        upper[++len_right] = temp % 10;
        temp /= 10;
    }

    if (len_left != len_right) {
        cout << string(len_right - 1, '9') << '\n';
        return ;
    }

    reverse(lower.begin() + 1, lower.begin() + len_left + 1);
    reverse(upper.begin() + 1, upper.begin() + len_right + 1);
    memset(dp, -1, sizeof dp);
    memset(numbers,0,sizeof numbers);

    auto dfs = [&](auto & self, int length, i64 current, int max_digit, int min_digit, bool limit_up, bool limit_down)->i64 {
        if (length == len_right + 1) return max_digit - min_digit;
        if (~dp[length][max_digit][min_digit][limit_up][limit_down])
            return dp[length][max_digit][min_digit][limit_up][limit_down];

        i64 best = 10;
        for (int digit = (limit_down ? lower[length] : 0); digit <= (limit_up ? upper[length] : 9); digit ++) {
            i64 res = self(self, length + 1, current / 10, max(max_digit, digit), min(min_digit, digit), limit_up & (digit == upper[length]), limit_down & (digit == lower[length]));
            i64 value = numbers[length + 1][max(max_digit, digit)][min(min_digit, digit)][limit_up & (digit == upper[length])][limit_down & (digit == lower[length])];
            if (res < best) {
                best = res;
                numbers[length][max_digit][min_digit][limit_up][limit_down] = digit * current + value;
            }
        }

        return dp[length][max_digit][min_digit][limit_up][limit_down] = best;
    };

    dfs(dfs, 1, offset, 0, 10, 1, 1);

    cout << numbers[1][0][10][1][1] << '\n';

}

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

    int test_cases;
    cin >> test_cases;
    while (test_cases--)
        solve();

    return 0;
}

F - 🐓🐓🐓🐓🐓🐓

CodeForces - 1547E

問題文

n個のマスとk個のエアコンがある。エアコンの位置には初期温度があり、エアコンは両方向に温度を影響させ、同じマスに複数の影響がある場合は最小値を取る。

解法

温度ポインタを保持し、左右からそれぞれ走査し、最小温度を更新しながら各マスの温度を計算する。

コード

#include<bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

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

    vector<int> temperatures(n + 1, 1 << 30), ac_positions(k + 1), ac_temps(k + 1);
    for (int i = 1; i <= k; i ++)
        cin >> ac_positions[i];
    for (int i = 1; i <= k; i ++) {
        cin >> ac_temps[i];
        temperatures[ac_positions[i]] = ac_temps[i];
    }

    int current = 1 << 30;
    for (int i = 1; i <= n; i ++) {
        current = min(++current, temperatures[i]);
        temperatures[i] = min(current, temperatures[i]);        
    }
    
    current = 1 << 30;
    for (int i = n; i >= 1; i --) {
        current = min(++current, temperatures[i]);
        temperatures[i] = min(current, temperatures[i]);
    }

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

}

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

    int test_cases;
    cin >> test_cases;
    while (test_cases--)
        solve();

    return 0;
}

G - 🐓🐓🐓🐓🐓🐓🐓

CodeForces - 1107C

問題文

文字ボタンの入力順序が与えられる。それぞれのボタンはAᵢのダメージを与えるが、同一ボタンはk回以上押せない。最大ダメージを達成するためにスキップするボタンの順序を求める。

解法

ボタンの順序を走査し、同じボタンの場合はヒープに保存する。異なるボタンに到達または最後に到達した際、ヒープから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<i64> damage(n);
    for (int i = 0; i < n; i ++) {
        cin >> damage[i];
    }

    string sequence;
    cin >> sequence;

    priority_queue<i64> heap;

    i64 total_damage = 0;
    for (int i = 0; i < n; i ++) {
        heap.push(damage[i]);
        if (i == n - 1 || i < n - 1 && sequence[i] != sequence[i + 1]) {
            int count = 0;
            while (count++ < k && heap.size()) {
                total_damage += heap.top();
                heap.pop();
            }
            while (heap.size()) heap.pop();
        }
    }

    cout << total_damage << '\n';

    return 0;
}

H - 🐓🐓🐓🐓🐓🐓🐓🐓

AtCoder - arc102_b

問題文

数Lが与えられる。1からnまでの有向グラフを構築し、1からnへのパス数をLとする。

解法

Lをビット表現に分解し、最も高いビットをkとする。1からkの間でi→i+1の辺を追加し、重みを0と2^(i-1)にする。これにより[0,2^k-1]のパス数を生成できる。Lの他のビットが1の場合は、iからnに重みを設定する。

コード

#include

using namespace std;

using i64 = long long;

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

    int bit_length = 0, L;
    cin >> L;

    while ((1 

タグ: AtCoder codeforces 数位DP floyd 最短経路

8月5日 02:59 投稿