Codeforces 教育ラウンド 66 (Div. 2 対象) 問題 A~F

A - ヒーローからゼロへ

シミュレーション問題です。

kで割れる場合は直接kで割り、そうでない場合は余りを引きます。

コードを見る

#include 
using namespace std;

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

    int t;
    cin >> t;

    while(t--) {
        long long n, k;
        cin >> n >> k;
        
        long long count = 0;
        while(n > 0){
            if(n % k == 0){
                n /= k;
                count++;
            }
            else{
                long long rem = n % k;
                count += rem;
                n -= rem;
            }
        }

        cout << count << "\n";
    }

    return 0;
}

B - オーバーフローをキャッチ!

スタックを利用して各操作の合計を維持します。`end`に遭遇した際には、直前の`for`からの合計を取り出し、そのループ回数を乗じます。オーバーフローが発生すれば即座に終了します。

コードを見る

#include 
using namespace std;

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

    int n;
    cin >> n;

    long long limit = (1LL << 32) - 1;
    stack st;
    vector> loops;

    for(int i=0;i> cmd;

        if(cmd == "add"){
            st.emplace(1, i);
        }
        else if(cmd == "for"){
            int a;
            cin >> a;
            loops.emplace_back(a, i);
        }
        else{
            long long sum = 0;
            while(!st.empty() && st.top().second >= loops.back().second){
                sum += st.top().first;
                st.pop();
            }
            auto [multiplier, _] = loops.back();
            loops.pop_back();

            if(sum * multiplier > limit){
                cout << "OVERFLOW!!!" << endl;
                return 0;
            }
            st.emplace(sum * multiplier, i);
        }
    }

    long long total = 0;
    while(!st.empty()){
        total += st.top().first;
        if(total > limit){
            cout << "OVERFLOW!!!" << endl;
            return 0;
        }
        st.pop();
    }

    cout << total << endl;
    return 0;
}

C - 電化

列挙アルゴリズムを使用します。

|a_i - x|はa_iとxの距離を表します。この場合、k+1番目に近い距離を見つける必要があります。これにより、先頭k個の点はxを中心に連続した区間となります。

コードを見る

#include 
using namespace std;

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

    int t;
    cin >> t;

    while(t--){
        int n, k;
        cin >> n >> k;

        vector<int> a(n);
        for(auto &x : a) cin >> x;

        int result = -1, min_distance = INT_MAX;
        for(int i=k;i

D - 配列分割

数学的アプローチを使用します。

S_kをkからnまでの後方和とし、p_iをi番目のサブ配列の開始インデックスとします。目的はS_p_1 + S_p_2 + ... + S_p_kを最大化することです。

コードを見る

#include 
using namespace std;

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

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

    vector<int> a(n);
    for(auto &x : a) cin >> x;

    vector<long long> suffix_sum(n);
    suffix_sum[n-1] = a[n-1];
    for(int i=n-2;i>=0;i--){
        suffix_sum[i] = suffix_sum[i+1] + a[i];
    }

    sort(suffix_sum.begin()+1, suffix_sum.end(), greater<long long>());

    long long answer = suffix_sum[0];
    for(int i=1;i

E - 最小セグメントカバー

倍増法を使用します。

f_{i,k}はiから2^kステップ移動した際に到達可能な最も遠い距離を示します。

コードを見る

#include 
using namespace std;

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

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

    int max_r = 0;
    vector> segments(n);
    for(auto &[l, r] : segments){
        cin >> l >> r;
        max_r = max(max_r, r);
    }

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

    vector<int> f(max_r+1, -1);
    int ptr = 0;
    for(int i=0;i<=max_r;i++){
        while(ptr < n && segments[ptr].first <= i){
            f[i] = max(f[i], segments[ptr].second);
            ptr++;
        }
    }

    int log_max = log2(max_r)+1;
    vector dp(max_r+1, vector<int>(log_max+1, -1));
    for(int i=0;i<=max_r;i++) dp[i][0] = f[i];

    for(int j=1;j<=log_max;j++) for(int i=0;i<=max_r;i++) if(dp[i][j-1]!=-1) dp[i][j] = dp[dp[i][j-1]][j-1];

    while(m--){
        int l, r;
        cin >> l >> r;

        if(r > max_r){
            cout << "-1\n";
            continue;
        }

        int steps = 0, current = l;
        for(int j=log_max;j>=0;j--){
            if(dp[current][j] != -1 && dp[current][j] < r){
                steps += (1<= r){
            steps++;
        }

        cout << (current >= r ? steps : -1) << "\n";
    }

    return 0;
}

F - 部分順列の数

XORハッシュを使用します。

[l,r]の範囲が1からr-l+1までの並びになる条件を満たすには、全ての要素が重複せず、最大値がr-l+1である必要があります。

コードを見る

#include 
using namespace std;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());

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

    int n;
    cin >> n;

    vector<int> a(n);
    for(auto &x : a) cin >> x;

    vector<unsigned long long> xor_hash(n+1);
    for(int i=1;i<=n;i++) xor_hash[i] = rng(), xor_hash[i] ^= xor_hash[i-1];

    long long answer = 0;
    auto process = [&](vector<int> &arr)->void{
        vector<int> prefix_hash(n+1);
        int max_val = 0;
        for(int i=0;i= max_val && (prefix_hash[i+1] ^ prefix_hash[i+1-max_val]) == xor_hash[max_val]){
                answer++;
            }
        }
    };

    process(a);
    reverse(a.begin(), a.end());
    process(a);

    answer -= count(a.begin(), a.end(), 1);
    cout << answer << "\n";

    return 0;
}

タグ: Algorithm competitive-programming C++

6月30日 19:38 投稿