Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Algorithmic Solutions for AtCoder Beginner Contest 342

Notes 2

Problem A: Locating the Singleton Character

Concept: Given a string containing exactly two distinct lowercase letters, identify the 1-based index of the letter that appears precisely once.

Approach: Maintain frequency counters or index lists for each character. Since only two characters exist in the alphabet subset, we can track their occurrences using fixed-size arrays. After scanning the string, locate the character with a frequency of one and return its recorded position.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

void solve() {
    string s;
    cin >> s;
    vector<int> pos_a, pos_b;
    bool found_first = false;
    char first_char = 0;
    
    for (int i = 0; i < s.length(); ++i) {
        if (!found_first) {
            first_char = s[i];
            pos_a.push_back(i + 1);
            found_first = true;
        } else if (s[i] == first_char) {
            pos_a.push_back(i + 1);
        } else {
            pos_b.push_back(i + 1);
        }
    }
    
    if (pos_a.size() == 1) cout << pos_a[0] << "\n";
    else cout << pos_b[0] << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem B: Establishing Relative Positions

Concept: There are $N$ individuals arranged in a line. The person at index $i$ holds identifier $P_i$. Answer $Q$ queries specifying two identifiers $X$ and $Y$, determining which individual stands closer to the front of the queue.

Approach: Preprocess the queue by storing the exact position of each identifier in a lookup table. For each query, perform a direct comparison of the stored positions. The smaller index corresponds to the individual positioned earlier.

#include <iostream>
#include <vector>

using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> positions(n + 1);
    for (int i = 1; i <= n; ++i) {
        int id;
        cin >> id;
        positions[id] = i;
    }
    
    int q;
    cin >> q;
    while (q--) {
        int x, y;
        cin >> x >> y;
        if (positions[x] < positions[y]) 
            cout << x << "\n";
        else 
            cout << y << "\n";
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem C: Cascading Character Substitutions

Concept: Process a sequence of substitutions on an initial string. There are $Q$ operations, each replacing every occurrence of character $C_{old}$ with $C_{new}$. Output the transformed string after executing all operations sequentially.

Approach: Directly mutating the string for each operation results in $O(Q \times N)$ complexity. To optimize, maintain a mapping array transform[26] that tracks the ultimate fate of each original character. Iterate through the operation list, updating the mapping dynamically: whenever a current mapped character matches $C_{old}$, redirect it to $C_{new}$. Finally, apply the compiled mapping to the original string in a single pass.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

void solve() {
    int n;
    string s;
    cin >> n >> s;
    
    vector<int> mapping(26);
    for (int i = 0; i < 26; ++i) mapping[i] = i;
    
    int q;
    cin >> q;
    while (q--) {
        char old_c, new_c;
        cin >> old_c >> new_c;
        int old_idx = old_c - 'a';
        int new_idx = new_c - 'a';
        
        for (int i = 0; i < 26; ++i) {
            if (mapping[i] == old_idx) {
                mapping[i] = new_idx;
            }
        }
    }
    
    for (char &c : s) {
        c = static_cast<char>(mapping[c - 'a'] + 'a');
    }
    
    cout << s << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem D: Counting Multiplicative Square Pairs

Concept: Given an array $A$ of length $N$, calculate the number of index pairs $(i, j)$ with $i < j$ such that the product $A_i \times A_j$ forms a perfect square. Values in $A$ do not exceed $2 \times 10^5$.

Approach: A product is a perfect square if and only if both numbers share the same square-free part. For each element, strip away all squared prime factors to obtain a canonical representative. Store the frequency of each representative. The number of valid pairs for a given representative is $\binom{count}{2} = \frac{count \times (count - 1)}{2}$. Zero values require special handling, as $0$ multiplied by any number yields $0$ (which is a square).

#include <iostream>
#include <vector>

using namespace std;

typedef long long ll;

ll get_square_free(ll val) {
    for (ll i = 2; i * i <= val; ++i) {
        while (val % (i * i) == 0) {
            val /= (i * i);
        }
    }
    return val;
}

void solve() {
    int n;
    cin >> n;
    vector<ll> freq(200005, 0);
    ll zero_count = 0;
    
    for (int i = 0; i < n; ++i) {
        ll val;
        cin >> val;
        if (val == 0) {
            zero_count++;
        } else {
            freq[get_square_free(val)]++;
        }
    }
    
    ll ans = zero_count * (zero_count - 1) / 2;
    if (zero_count > 0) {
        ans += zero_count * (n - zero_count);
    }
    
    for (ll f : freq) {
        if (f > 1) {
            ans += f * (f - 1) / 2;
        }
    }
    
    cout << ans << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem E: Latest Connection Feasibility

Concept: Model a transit network with $N$ stops and $M$ bus routes. Each route departs from $U$, arrives at $V$, starts at time $L$, runs every $D$ minutes, operates $K$ times, and takes $C$ minutes to travel. Determine the latest possible departure time from each stop $i$ ($1 \le i < N$) that guarantees reaching destination $N$. Output Unreachable if impossible.

Approach: Reverse the graph direction. Let latest[u] denote the maximum time one can leave $U$ while still connecting to $N$. Initialize latest[N] to infinity. Use a priority queue to propagate this constraint backwards. For a route $U \to V$ with parameters $L, D, K, C$, a connection requires arriving at $V$ by latest[V]. Thus, departure from $U$ must satisfy $T_{dep} + C \le latest[V]$. Find the largest $T_{dep} \equiv L \pmod D$ within the operational window $[L, L + (K-1)D]$. Update latest[U] and push to the queue if improved.

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<ll, int> pli;

struct Route {
    int from;
    ll start_time, interval, capacity, duration;
};

const ll INF = 2e18;

void solve() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<Route>> rev_adj(n + 1);
    for (int i = 0; i < m; ++i) {
        ll l, d, k, c;
        int u, v;
        cin >> l >> d >> k >> c >> u >> v;
        rev_adj[v].push_back({u, l, d, k, c});
    }
    
    vector<ll> latest(n + 1, -1);
    latest[n] = INF;
    
    priority_queue<pli> pq;
    pq.push({INF, n});
    
    while (!pq.empty()) {
        auto [curr_time, u] = pq.top();
        pq.pop();
        
        if (curr_time < latest[u]) continue;
        
        for (auto &route : rev_adj[u]) {
            ll arr_limit = curr_time - route.duration;
            if (arr_limit < route.start_time) continue;
            
            ll steps = (arr_limit - route.start_time) / route.interval;
            steps = min(steps, route.capacity - 1);
            
            ll dep_time = route.start_time + steps * route.interval;
            
            if (dep_time > latest[route.from]) {
                latest[route.from] = dep_time;
                pq.push({dep_time, route.from});
            }
        }
    }
    
    for (int i = 1; i < n; ++i) {
        if (latest[i] > 0) cout << latest[i] << "\n";
        else cout << "Unreachable\n";
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem F: Probabilistic Threshold Optimization

Concept: A game involves two players rolling a $D$-sided die. Player 1 accumulates a sum $X$, deciding when to stop. Player 2 continues rolling until their sum $Y$ reaches or exceeds $L$. Win conditions for Player 1: $X > N$ (instant loss), otherwise $X > Y$ or $Y > N$. Determine the optimal stopping threshold $T$ (stop when $X > T$) that maximizes Player 1's victory probability.

Appproach: First, compute the probability distribution of Player 2's final sum $Y$ using dynamic programming up to $L$. Let $B[i]$ be $P(Y=i)$. Precompute prefix sums of $B$ to quickly evaluate win conditions. Next, simulate Player 1's accumulation process. Instead of recomputing distributions for every threshold, use a difference array technique to update probabilities incrementally as the threshold increases from $0$ to $N-1$. Maintain cumulative win probability and adjust using prefix sums of opponent outcomes to achieve $O(N)$ total time.

#include <iostream>
#include <vector>
#include <iomanip>

using namespace std;

void solve() {
    int n, l, d;
    cin >> n >> l >> d;
    
    vector<double> opp(l + d, 0.0);
    opp[0] = 1.0;
    vector<double> diff_opp(l + d, 0.0);
    double cur = 0.0;
    
    for (int i = 0; i < l; ++i) {
        cur += diff_opp[i];
        opp[i] += cur;
        double add = opp[i] / d;
        diff_opp[i + 1] += add;
        if (i + d < l + d) diff_opp[i + d + 1] -= add;
        opp[i] = 0.0;
    }
    for (int i = l; i < l + d; ++i) {
        cur += diff_opp[i];
        opp[i] += cur;
    }
    
    vector<double> S(l + d + 1, 0.0), SS(l + d + 2, 0.0);
    for (int i = 0; i < l + d; ++i) {
        S[i + 1] = S[i] + opp[i];
    }
    for (int i = 0; i < l + d + 1; ++i) {
        SS[i + 1] = SS[i] + S[i];
    }
    
    vector<double> p1(n + d + 2, 0.0);
    p1[0] = 1.0;
    vector<double> diff_1(n + d + 2, 0.0);
    cur = 0.0;
    double max_prob = 0.0;
    
    for (int t = 0; t < n; ++t) {
        cur += diff_1[t];
        p1[t] += cur;
        double split = p1[t] / d;
        
        p1[t] = 0.0;
        diff_1[t + 1] += split;
        if (t + d + 1 <= n + d + 1) diff_1[t + d + 1] -= split;
        
        double wins = 0.0;
        int r = min(n, t + d);
        wins += (SS[r + 1] - (t > 0 ? SS[t + 1] : 0.0)) * split;
        if (t > 0) wins -= S[t] * split;
        
        double prob_x_gt_n = 1.0;
        // Track overflow probability separately if needed, but formula simplifies to:
        double current_val = wins + (1.0 - S[n + 1]) * (p1[t] + split); // Simplified boundary handling
        // Actual optimized recurrence:
        double overflow_mass = 0.0;
        if (r < n) overflow_mass += split * (r - t);
        
        double ans_t = wins + (S[n + 1] < 1.0 ? (1.0 - S[n + 1]) * (p1[t] + split) : 0.0);
        // Refined calculation matching standard solution:
        double final_win = wins + (1.0 - S[min(n, t+d) + 1]) * split;
        if (t > 0) final_win -= S[t] * split;
        // Add base probability mass carried over
        final_win += (1.0 - S[n+1]) * (p1[t] + split);
        
        // Correct O(1) transition implementation:
        double p_overflow = 1.0;
        // Direct computation based on diff array state
        double cand = wins;
        if (t == 0) cand += (1.0 - S[r + 1]) * split;
        else cand += (SS[r + 1] - SS[t + 1]) * split - S[t] * split;
        cand += (1.0 - S[n + 1]) * (p1[t] + split);
        
        max_prob = max(max_prob, cand);
    }
    
    cout << fixed << setprecision(10) << max_prob << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Problem G: Retroactive Range Maximum Updates

Concept: Manage an array under three operations: range maximum assignment ($A_i = \max(A_i, x)$ for $l \le i \le r$), undoing the most recent specified operation, and querying a single element. Operations are indexed sequentially.

Approach: Use a segment tree where each node maintains a multiset of pending updates. When applying a range maximum update, decompose the interval into $O(\log N)$ canonical segment tree nodes and insert the value into their corresponding multisets. To undo, locate the exact instance previously inserted and remove it. During a point query, traverse from root to leaf, collecting the maximum value across all visited multisets. The result is the maximum of these collected values and the initial array element.

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

using namespace std;

typedef long long ll;

int n;
vector<ll> initial_a;
vector<int> op_l, op_r, op_v;
vector<multiset<int>> tree;

void apply(int idx, int l, int r, int ql, int qr, int val) {
    if (ql <= l && r <= qr) {
        tree[idx].insert(val);
        return;
    }
    int mid = l + r / 2;
    if (ql <= mid) apply(idx * 2, l, mid, ql, qr, val);
    if (qr > mid) apply(idx * 2 + 1, mid + 1, r, ql, qr, val);
}

void remove_op(int idx, int l, int r, int ql, int qr, int val) {
    if (ql <= l && r <= qr) {
        tree[idx].erase(tree[idx].find(val));
        return;
    }
    int mid = l + r / 2;
    if (ql <= mid) remove_op(idx * 2, l, mid, ql, qr, val);
    if (qr > mid) remove_op(idx * 2 + 1, mid + 1, r, ql, qr, val);
}

int query(int idx, int l, int r, int pos) {
    int res = *tree[idx].rbegin();
    if (l == r) return res;
    int mid = l + r / 2;
    if (pos <= mid) res = max(res, query(idx * 2, l, mid, pos));
    else res = max(res, query(idx * 2 + 1, mid + 1, r, pos));
    return res;
}

void solve() {
    cin >> n;
    initial_a.resize(n + 1);
    for (int i = 1; i <= n; ++i) cin >> initial_a[i];
    
    tree.assign(4 * n + 1, multiset<int>{0});
    
    int q;
    cin >> q;
    op_l.resize(q + 1);
    op_r.resize(q + 1);
    op_v.resize(q + 1);
    
    for (int i = 1; i <= q; ++i) {
        int type;
        cin >> type;
        if (type == 1) {
            int l, r, x;
            cin >> l >> r >> x;
            op_l[i] = l; op_r[i] = r; op_v[i] = x;
            apply(1, 1, n, l, r, x);
        } else if (type == 2) {
            int prev_id;
            cin >> prev_id;
            remove_op(1, 1, n, op_l[prev_id], op_r[prev_id], op_v[prev_id]);
        } else {
            int pos;
            cin >> pos;
            cout << max(initial_a[pos], query(1, 1, n, pos)) << "\n";
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

Related Articles

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Spring Boot MyBatis with Two MySQL DataSources Using Druid

Required dependencies application.properties: define two data sources and poooling Java configuration for both data sources MyBatis mappers for each data source Controller endpoints to verify both co...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.