Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Computational Solutions for AtCoder Beginner Contest 006

Tech May 15 1

Divisibility and Digit Extraction

The objective is to determine if a given integer $N$ is either divisible by 3 or contains the digit '3'.

A direct approach involves checking the modulo and then iteratively extracting digits. The time complexity for this is $O(\log_{10} N)$.

bool is_lucky(long long val) {
    if (val % 3 == 0) return true;
    std::string s = std::to_string(val);
    return s.find('3') != std::string::npos;
}

Mathematical Note: Divisibility Rules

A number is divisible by 3 (or 9) if and only if the sum of its digits is divisible by 3 (or 9). This is because any power of 10 can be expressed as $10^k = (10^k - 1) + 1$, where $10^k - 1$ is always a sequence of $9$s (e.g., $99, 999$), wich is divisible by both 3 and 9.

Extended Variation: Counting up to N

If the problem asks for the count of non-negatiev integers $m \le n$ that satisfy the condition, we can use Digit Dynamic Programming. We calculate the complement: the count of numbers that are NOT divisible by 3 and do NOT contain the digit '3'.

long long solve_digit_dp(int n) {
    std::string s = std::to_string(n);
    int len = s.size();
    long long memo[20][2][3];
    memset(memo, -1, sizeof(memo));

    auto dfs = [&](auto self, int pos, bool is_less, int remainder) -> long long {
        if (pos == len) return remainder != 0;
        if (memo[pos][is_less][remainder] != -1) return memo[pos][is_less][remainder];

        long long count = 0;
        int limit = is_less ? 9 : s[pos] - '0';
        for (int d = 0; d <= limit; ++d) {
            if (d == 3) continue;
            count += self(self, pos + 1, is_less || (d < limit), (remainder * 10 + d) % 3);
        }
        return memo[pos][is_less][remainder] = count;
    };
    return n - dfs(dfs, 0, false, 0);
}

Efficient Calculation of the Tribonacci Sequence

Given the recurrence $f(n) = f(n-1) + f(n-2) + f(n-3)$ with base cases $f(1)=0, f(2)=0, f(3)=1$, compute $f(n) \pmod{10007}$.

Linear Time Approach

For $n \le 10^6$, an iterative $O(n)$ solution is sufficient:

int get_tribonacci(int n) {
    if (n <= 2) return 0;
    if (n == 3) return 1;
    std::vector<int> dp(n + 1);
    dp[1] = dp[2] = 0; dp[3] = 1;
    for (int i = 4; i <= n; ++i) {
        dp[i] = (dp[i-1] + dp[i-2] + dp[i-3]) % 10007;
    }
    return dp[n];
}

Logarithmic Time Approach

For very large $n$, we use matrix exponentiation. The state transition can be represented as:

$$\begin{bmatrix} f_{i+1} \ f_{i} \ f_{i-1} \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 \ 1 & 0 & 0 \ 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} f_{i} \ f_{i-1} \ f_{i-2} \end{bmatrix}$$

By computing $M^{n-3}$ where $M$ is the transformation matrix, we can find $f(n)$ in $O(3^3 \log n)$.

Linear Systems for Counting Leg Combinations

With $N$ individuals consisting of adults (2 legs), elders (3 legs), and babies (4 legs), given total legs $M$, find any valid count $(a, b, c)$.

Let $a, b, c$ be the counts for adults, elders, and babies respectively:

  1. $a + b + c = N$
  2. $2a + 3b + 4c = M$

Subtracting twice the first equation from the second: $(2a + 3b + 4c) - 2(a + b + c) = M - 2N$ $b + 2c = M - 2N$

We can iterate through possible values of $c$ and solve for $b$ and $a$:

void solve_legs(int n, int m) {
    for (int c = 0; c <= n; ++c) {
        int b = m - 2 * n - 2 * c;
        int a = n - b - c;
        if (b >= 0 && a >= 0 && (2 * a + 3 * b + 4 * c == m)) {
            std::cout << a << " " << b << " " << c << std::endl;
            return;
        }
    }
    std::cout << "-1 -1 -1" << std::endl;
}

Sorting with Minimum Moves and Longest Increasing Subsequence

Given a permutation of $N$ cards, find the minimum number of cards to move to make the sequence ascending. A single move consists of picking a card and inserting it anywhere.

The minimum number of moves requierd to sort an array by moving elements is $N - L$, where $L$ is the length of the Longest Increasing Subsequence (LIS). Elements belonging to the LIS remain fixed, while all other elements are repositioned around them.

Binary Search Optimization ($O(N \log N)$)

We maintain an array tails where tails[i] is the smallest tail of all increasing subsequences of length i+1.

int min_moves_to_sort(int n, const std::vector<int>& cards) {
    std::vector<int> tails;
    for (int x : cards) {
        auto it = std::lower_bound(tails.begin(), tails.end(), x);
        if (it == tails.end()) {
            tails.push_back(x);
        } else {
            *it = x;
        }
    }
    return n - tails.size();
}

Segment Tree / Fenwick Tree Approach ($O(N \log M)$)

If the values are within a specific range, we can use a Fenwick tree to store the maximum LIS length ending at a certain value. For each element $x$, we query the range $[1, x-1]$ for the maximum value $v$ and update the tree at position $x$ with $v+1$.

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.