Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Algorithmic Strategies for Competitive Programming Challenges

Tech 3

Composite Coloring with Bounded Prime Factors

Problem Specification

Given a sequence of composite integers $a$ of length $n$, assign colors such that the greatest common divisor of all numbers sharing the same color exceeds $1$. The total number of distinct colors must not exceed $11$. Constraints: $a_i \le 1000, n \le 1000$.

Algorithmic Approach

Every composite number $x \le 1000$ possesses at least one prime divisor $p \le \sqrt{1000} \approx 31.6$. There are exactly eleven primes within this range. By assigning each integer a color corresponding to its smallest prime factor, we guarantee that all elements of a specific color share a common divisor greater than one. The implementation maps these prime factors to a contiguous color index range to satisfy the output requirement.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_VAL = 1005;
int min_prime_factor[MAX_VAL], color_remap[MAX_VAL];
vector<int> candidate_primes;

void precompute_factors() {
    for (int i = 2; i * i < MAX_VAL; ++i) {
        if (min_prime_factor[i] == 0) {
            for (int j = i * i; j < MAX_VAL; j += i) {
                if (min_prime_factor[j] == 0) min_prime_factor[j] = i;
            }
        }
    }
    for (int i = 2; i < MAX_VAL; ++i) {
        if (min_prime_factor[i] == 0) min_prime_factor[i] = i;
        if (min_prime_factor[i] <= 32) candidate_primes.push_back(min_prime_factor[i]);
    }
    sort(candidate_primes.begin(), candidate_primes.end());
    candidate_primes.erase(unique(candidate_primes.begin(), candidate_primes.end()), candidate_primes.end());
}

void solve_instance() {
    int n; cin >> n;
    vector<int> input_seq(n), output_colors(n);
    for (int &val : input_seq) cin >> val;
    
    vector<int> raw_colors(n);
    for (int idx = 0; idx < n; ++idx) raw_colors[idx] = min_prime_factor[input_seq[idx]];
    
    int palette_size = 0;
    memset(color_remap, 0, sizeof(color_remap));
    for (int idx = 0; idx < n; ++idx) {
        int p = raw_colors[idx];
        if (!color_remap[p]) color_remap[p] = ++palette_size;
        output_colors[idx] = color_remap[p];
    }
    
    cout << palette_size << '\n';
    for (int i = 0; i < n; ++i) cout << output_colors[i] << " \n"[i == n - 1];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    precompute_factors();
    int cases; cin >> cases;
    while (cases--) solve_instance();
    return 0;
}

Handshake Sequence Feasibility

Problem Specification

A group of $n$ individuals enters a hall sequentially, with each person shaking hands with everyone currently present. Given a target sequence of handshake counts, determine if a valid entry/exit order exists. Constraints: $n \le 2 \times 10^5$.

Algorithmic Approach

The handshake count at any moment equals the number of people in the room. Valid transitions involve either incrementing the count by $1$ (a new person enters) or decrementing by $3$ (three people leave simultaneously). By grouping initial indices into buckets based on their handshake count, we can greedily traverse the sequence. When a required bucket is empty, we backtrack in steps of three until a valid group is found or the state drops below zero.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_N = 200005;
vector<int> position_buckets[MAX_N];
int final_order[MAX_N];

void evaluate_sequence() {
    int n; cin >> n;
    for (int i = 1; i <= n; ++i) {
        int handshakes;
        cin >> handshakes;
        position_buckets[handshakes].push_back(i);
    }

    int current_level = 0, placed_count = 1;
    while (placed_count <= n) {
        while (current_level > 0 && position_buckets[current_level].empty()) {
            current_level -= 3;
        }
        if (position_buckets[current_level].empty()) {
            cout << "Impossible\n";
            return;
        }
        final_order[placed_count++] = position_buckets[current_level].back();
        position_buckets[current_level].pop_back();
        current_level++;
    }

    cout << "Possible\n";
    for (int i = 1; i <= n; ++i) {
        cout << final_order[i] << " \n"[i == n];
    }
}

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

Maximum Greater-Than-or-Equal Subarray Sum

Problem Specification

Determine if an array $A$ satisfies the condition that for all subarrays, the maximum element is at least the subarray sum. Constraints: $n \le 2 \times 10^5$.

Algorithmic Approach

The condition fails if any contiguous segment has a positive total sum strictly exceeding its peak value. We process the array bidirectionally using a monotonic decreasing stack. For each element acting as a segment maximum, we verify whether the accumulated prefix or suffix sums within its dominance range yield a positive value. If a violation is detected during stack compression, the array is invalid.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_LEN = 200010;
int sequence[MAX_LEN], prefix_acc[MAX_LEN], suffix_acc[MAX_LEN];

void check_validity() {
    int n; cin >> n;
    prefix_acc[0] = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> sequence[i];
        prefix_acc[i] = prefix_acc[i-1] + sequence[i];
    }
    suffix_acc[n+1] = 0;
    for (int i = n; i >= 1; --i) suffix_acc[i] = suffix_acc[i+1] + sequence[i];

    struct StackFrame { int value; int index; };
    vector<StackFrame> stack_vec;
    stack_vec.reserve(n);

    auto process_left = [&]() {
        stack_vec.clear();
        for (int i = 1; i <= n; ++i) {
            int max_segment = 0;
            while (!stack_vec.empty() && stack_vec.back().value <= sequence[i]) {
                max_segment = max(max_segment, prefix_acc[i-1] - prefix_acc[stack_vec.back().index - 1]);
                stack_vec.pop_back();
            }
            if (max_segment > 0) {
                cout << "NO\n";
                return true;
            }
            stack_vec.push_back({sequence[i], i});
        }
        return false;
    };

    if (process_left()) return;

    stack_vec.clear();
    for (int i = n; i >= 1; --i) {
        int max_segment = 0;
        while (!stack_vec.empty() && stack_vec.back().value <= sequence[i]) {
            max_segment = max(max_segment, suffix_acc[i+1] - suffix_acc[stack_vec.back().index + 1]);
            stack_vec.pop_back();
        }
        if (max_segment > 0) {
            cout << "NO\n";
            return;
        }
        stack_vec.push_back({sequence[i], i});
    }
    cout << "YES\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t; cin >> t;
    while (t--) check_validity();
    return 0;
}

Optimal Stack Operations Maximization

Problem Specification

Process an array sequential, choosing between pushing an element onto a stack or popping if non-empty. Maximize the final stack sum.

Algorithmic Approach

Commutativity of push-pop sequences reveals that any valid operation stream can be reduced to either accumulating the current value or discarding both the current and preceding elements. This yields the recurrence $dp[i] = \max(dp[i-1] + val_i, dp[i-2])$, solvable in linear time with constant space optimization.

Implemantation

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    vector<int> values(n);
    for (int &x : values) cin >> x;

    if (n == 0) { cout << 0; return 0; }
    vector<int> dp(n + 1, 0);
    dp[1] = values[0];
    
    for (int i = 2; i <= n; ++i) {
        dp[i] = max(dp[i-1] + values[i-1], dp[i-2]);
    }
    
    cout << dp[n] << '\n';
    return 0;
}

Minimum Adjacent Swap Cost

Problem Specification

Sort a permutation by swapping adjacent elements. The cost of each swap equals the left element's value. Find the minimum total cost. Constraints: $n \le 2 \times 10^5$.

Algorithmic Approach

The total expense corresponds to the cumulative displacement cost of each element moving past larger values. By tracking initial positions and utilizing a Fenwick tree to count preceding larger elements, we compute the required shifts. The solution aggregates positional deltas weighted by inversion counts to derive the minimal expanse.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_SZ = 200005;
int fenwick_tree[MAX_SZ], initial_pos[MAX_SZ], inv_count[MAX_SZ];
struct Element { int val; int idx; } elems[MAX_SZ];

int query_bit(int idx) {
    int res = 0;
    for (; idx > 0; idx -= idx & -idx) res += fenwick_tree[idx];
    return res;
}

void update_bit(int idx, int delta) {
    for (; idx < MAX_SZ; idx += idx & -idx) fenwick_tree[idx] += delta;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> elems[i].val;
        elems[i].idx = i;
        inv_count[i] = query_bit(MAX_SZ - 1) - query_bit(elems[i].val);
        update_bit(elems[i].val, 1);
    }

    sort(elems + 1, elems + n + 1, [](const Element &a, const Element &b) {
        return a.val < b.val;
    });

    long long total_cost = 0;
    for (int i = 1; i <= n; ++i) {
        long long displacement = elems[i].idx - inv_count[elems[i].idx];
        long long target = i;
        total_cost += (displacement - 1 + target) * abs(displacement - target) / 2;
    }
    cout << total_cost << '\n';
    return 0;
}

Optimal Binary Flip Sequences

Problem Specification

Transform binary array $A$ to match $B$ using element flips. Each operation adds a cost proportional to current $1$s wieghted by array $C$. Minimize total expense.

Algorithmic Approach

Categorize indices into three groups: forced $1 \to 0$, forced $0 \to 1$, and neutral $1 \to 1$ candidates. Sorting costs and utilizing two Fenwick trees (sum and count) allows greedy insertion of neutral flips at optimmal positions. Iteratively updating the cumulative expense while tracking the global minimum yields the solution.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_DIM = 1000005;
struct Fenwick {
    int tree[MAX_DIM];
    void add(int idx, int delta) {
        for (; idx < MAX_DIM; idx += idx & -idx) tree[idx] += delta;
    }
    int sum(int idx) {
        int res = 0;
        for (; idx > 0; idx -= idx & -idx) res += tree[idx];
        return res;
    }
} val_tree, cnt_tree;

int n, src[MAX_DIM], tgt[MAX_DIM], costs[MAX_DIM];
vector<int> group_10, group_01, group_11;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> src[i];
    for (int i = 1; i <= n; ++i) cin >> tgt[i];
    for (int i = 1; i <= n; ++i) {
        cin >> costs[i];
        if (src[i] == 1 && tgt[i] == 0) group_10.push_back(costs[i]);
        else if (src[i] == 0 && tgt[i] == 1) group_01.push_back(costs[i]);
        else if (src[i] == 1 && tgt[i] == 1) group_11.push_back(costs[i]);
    }

    auto desc = [](int a, int b) { return a > b; };
    sort(group_10.begin(), group_10.end(), desc);
    sort(group_01.begin(), group_01.end());
    sort(group_11.begin(), group_11.end(), desc);

    long long base_expense = 0, suffix_sum = 0, best_res = 0;
    vector<long long> suffix_acc(group_11.size() + 1, 0);
    for (int i = group_11.size() - 1; i >= 0; --i) suffix_acc[i] = suffix_acc[i+1] + group_11[i];

    for (size_t i = 0; i < group_10.size(); ++i) base_expense += group_10[i] * i;
    for (size_t i = 0; i < group_01.size(); ++i) base_expense += group_01[i] * (group_01.size() - i);
    for (int c : group_11) base_expense += 1LL * c * (group_10.size() + group_01.size());
    best_res = base_expense;

    for (int c : group_10) { val_tree.add(c, c); cnt_tree.add(c, 1); }
    for (int c : group_01) { val_tree.add(c, c); cnt_tree.add(c, 1); }

    for (int i = 0; i < group_11.size(); ++i) {
        int cost = group_11[i];
        base_expense += val_tree.sum(cost);
        base_expense += 1LL * (group_10.size() + group_01.size() + 2*(i+1) - cnt_tree.sum(cost) - 1) * cost;
        base_expense -= 1LL * (group_10.size() + group_01.size() + 2*i) * cost;
        base_expense += suffix_acc[i+1] * 2;
        best_res = min(best_res, base_expense);
        val_tree.add(cost, cost);
        cnt_tree.add(cost, 1);
    }
    cout << best_res << '\n';
    return 0;
}

Key Management Profit Maximization

Problem Specification

Open $n$ chests sequentially. Each can be opened with fixed cost $k$ or for free (halving all subsequent rewards). Maximize net profit. $n \le 10^5$.

Algorithmic Approach

Free openings halve rewards exponentially; applying them more than $\log_2(10^9) \approx 30$ times reduces values to zero. A dynamic programming approach tracks the current chest index and the number of free operations used, capped at $30$. Transitions evaluate paying the fixed fee versus consuming a free operation, propagating the maximum achievable profit.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_CHESTS = 100005;
constexpr int MAX_FREE = 32;
long long base_vals[MAX_CHESTS][MAX_FREE], dp_table[MAX_CHESTS][MAX_FREE];

void compute_max_profit() {
    int n, fee; cin >> n >> fee;
    for (int i = 1; i <= n; ++i) {
        cin >> base_vals[i][0];
        for (int j = 1; j < MAX_FREE; ++j) base_vals[i][j] = base_vals[i][j-1] / 2;
    }

    for (int i = 0; i <= n; ++i) fill(dp_table[i], dp_table[i] + MAX_FREE, -2e18);
    dp_table[0][0] = 0;

    for (int i = 1; i <= n; ++i) {
        int limit = min(i, 30);
        for (int j = 0; j <= limit; ++j) {
            dp_table[i][j] = max(dp_table[i][j], dp_table[i-1][j] + base_vals[i][j] - fee);
            if (j > 0) dp_table[i][j] = max(dp_table[i][j], dp_table[i-1][j-1] + base_vals[i][j]);
        }
        dp_table[i][30] = max(dp_table[i][30], dp_table[i-1][30]);
    }

    long long ans = -2e18;
    for (int j = 0; j <= 30; ++j) ans = max(ans, dp_table[n][j]);
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int cases; cin >> cases;
    while (cases--) compute_max_profit();
    return 0;
}

Lattice Path Counting in Rectangles

Problem Specification

Count monotonic paths from $(0,0)$ to any coordinate within rectangle $[r_1, r_2] \times [c_1, c_2]$. Modulo arithmetic applies.

Algorithmic Approach

Paths to $(r,c)$ follow $\binom{r+c}{c}$. Summing over the rectangle reduces to evaluating a $2$D prefix sum of binomial coefficients. The hockey-stick identity $\sum_{i=0}^r \binom{i+c}{c} = \binom{r+c+1}{c+1}$ simplifies the inner summation, enabling $O(1)$ evaluation per rectangle corner after factorial precomputation.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MOD = 1000000007;
constexpr int MAX_N = 400005;
long long fact[MAX_N], inv_fact[MAX_N], mod_inv[MAX_N];

void prepare_combinatorics() {
    fact[0] = inv_fact[0] = 1;
    mod_inv[1] = 1;
    for (int i = 2; i < MAX_N; ++i) mod_inv[i] = MOD - (MOD/i) * mod_inv[MOD%i] % MOD;
    for (int i = 1; i < MAX_N; ++i) {
        fact[i] = fact[i-1] * i % MOD;
        inv_fact[i] = inv_fact[i-1] * mod_inv[i] % MOD;
    }
}

long long nCr(int n, int r) {
    if (r < 0 || r > n) return 0;
    return fact[n] * inv_fact[r] % MOD * inv_fact[n-r] % MOD;
}

long long rectangle_paths(int r, int c) {
    return (nCr(r + c + 1, c + 1) - 1 + MOD) % MOD;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int r1, c1, r2, c2;
    cin >> r1 >> c1 >> r2 >> c2;
    prepare_combinatorics();
    long long ans = (rectangle_paths(r2, c2) - rectangle_paths(r1-1, c2) - rectangle_paths(r2, c1-1) + rectangle_paths(r1-1, c1-1)) % MOD;
    cout << (ans + 2*MOD) % MOD << '\n';
    return 0;
}

Power Up Merging Configurations

Problem Specification

Given a multiset, merge pairs of equal integers $x$ into $x+1$. Count distinct final configurations.

Algorithmic Approach

Model the merging process by tracking propagation counts across integer levels. Dynamic programming computes valid states for each value, utilizing prefix sums to transition efficiently. The algorithm aggregates counts while ensuring merge constraints are satisfied.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MOD = 998244353;
constexpr int MAX_VAL = 300100;
int freq[MAX_VAL], dp_state[MAX_VAL], prefix_sum[MAX_VAL];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; cin >> n;
    int min_v = 1e9, max_v = 0;
    for (int i = 0; i < n; ++i) {
        int x; cin >> x;
        freq[x]++;
        min_v = min(min_v, x);
        max_v = max(max_v, x);
    }

    int carry = freq[min_v];
    fill(prefix_sum, prefix_sum + carry + 1, 1);

    for (int val = min_v + 1; val <= max_v + 60; ++val) {
        carry = freq[val] + carry / 2;
        for (int i = 0; i < freq[val]; ++i) dp_state[i] = 0;
        for (int i = freq[val]; i <= carry; ++i) dp_state[i] = prefix_sum[(i - freq[val]) * 2];
        
        prefix_sum[carry] = dp_state[carry];
        for (int i = carry - 1; i >= 0; --i) {
            prefix_sum[i] = (prefix_sum[i+1] + dp_state[i]) % MOD;
        }
    }
    cout << prefix_sum[0] << '\n';
    return 0;
}

Segmentation Product Summation

Problem Specification

Partition an $n$-digit number into segments. Sum the products of segment values across all partitions. Modulo $998244353$.

Algorithmic Approach

Let $dp[i]$ denote the total sum for the first $i$ digits. The recurrence $dp[i] = \sum_{j=0}^{i-1} dp[j] \times S(j+1, i)$ can be optimized by separating the digit contribution and scaling previous results. This simplifies to $dp[i] = 10 \cdot dp[i-1] + d_i \cdot \sum_{j=0}^{i-1} dp[j]$, enabling linear execution.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MOD = 998244353;
constexpr int MAX_LEN = 200005;
long long dp_val[MAX_LEN], prefix_acc[MAX_LEN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n; string s;
    cin >> n >> s;
    s = " " + s;
    int digit = s[1] - '0';
    dp_val[1] = digit;
    prefix_acc[1] = 1;

    for (int i = 2; i <= n; ++i) {
        digit = s[i] - '0';
        dp_val[i] = (dp_val[i-1] * 10 + prefix_acc[i-1] * digit) % MOD;
        prefix_acc[i] = (prefix_acc[i-1] + dp_val[i-1]) % MOD;
        dp_val[i] = (dp_val[i] + dp_val[i-1] * digit) % MOD;
    }
    cout << dp_val[n] << '\n';
    return 0;
}

Range Zero Transformation Queries

Problem Specification

Given $q$ queries, determine if a subarray $[l, r]$ can be zeroed using length-$k$ uniform increments.

Algorithmic Approach

Transforming the array via difference sequences reveals that operations affect indices modulo $k$ independently. By maintaining prefix sums of the difference array partitioned by residue class, each query validates consistency across relevant classes. Boundary conditions and modulo alignment are verified in constant time.

Implementation

#include <bits/stdc++.h>
using namespace std;

constexpr int MAX_N = 200005;
constexpr int MAX_K = 10;
long long arr[MAX_N], diff_prefix[MAX_N][MAX_K];

void handle_query() {
    int l, r, n, k;
    cin >> l >> r;
    for (int mod_res = max(r - k + 2, l); mod_res <= r; ++mod_res) {
        long long val = diff_prefix[mod_res][mod_res % k] - diff_prefix[l-1][mod_res % k];
        if (mod_res % k == l % k) val += arr[l-1];
        if (val != 0) {
            cout << "No\n";
            return;
        }
    }
    cout << "Yes\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) {
        cin >> arr[i];
        for (int j = 0; j < k; ++j) diff_prefix[i][j] = diff_prefix[i-1][j];
        diff_prefix[i][i % k] += arr[i] - arr[i-1];
    }
    int q; cin >> q;
    while (q--) handle_query();
    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

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