AtCoder Beginner Contest 457

atcodercompetitive programming

Introduction

The Polaris.AI Programming Contest 2026 (AtCoder Beginner Contest 457) concluded this past Saturday. After taking a virtual contest, I find the problems very cute and interesting. For problems A, B, C, D, and E, I was comfortable with the ideas and solved the problems fairly quickly. Problems F and G were harder but contain some nice ideas. Here I will share some of my thought processes.

By the way, as part of my journey of learning new programming languages, I write all my code in Kotlin. I am also working on a snippet collection for competitive programming in Kotlin as I go.

A. Array

Problem. Given an array 𝐴 of length 𝑁 and integer 𝑋, find 𝐴𝑋.

Analysis. Ask a toddler on the street.

B. Arrays

Problem. Given an array 𝐴 consisting of 𝑁 arrays and two integers 𝑋 and π‘Œ, output 𝐴𝑋,π‘Œ.

Analysis. Ask a Chinese kindergarten student.

C. Long Sequence

Problem. You are given an array 𝐴 consisting of 𝑁 arrays, an integer array 𝐢 of length 𝑁, and an integer 𝐾. If 𝐡 is the array constructed by joining 𝐢𝑖 copies of 𝐴𝑖 for each 𝑖=1,2,…,𝑁 in this order, find 𝐡𝐾.

Constraints. βˆ‘π‘–=1𝑁|𝐴𝑖|≀2Γ—105, 1≀𝐴𝑖,𝑗≀109, 1≀𝐢𝑖≀109.

Analysis. Simulating can take over 1014 steps, which is not feasible. However, we only need to find a single element, so we can use modular arithmetic to skip over almost all of the copies. Let us first find out which array contains the answer; then, we can figure out where in that array the answer lies using the remainder. For some π‘š, define

as the number of elements remaining after skipping over 𝐴1,…,π΄π‘š. There must exist a unique π‘š such that

so the answer will be an element of π΄π‘š+1. To find which one it is, we can take 𝑖=π‘…π‘šmod|π΄π‘š+1|, and π΄π‘š+1,𝑖 is our answer.

D. Raise Minimum

Problem. You are given an array 𝐴 of length 𝑁 and an integer 𝐾. In one operation, you can choose some 1≀𝑖≀𝑁 and set 𝐴𝑖≔𝐴𝑖+𝑖. After at most 𝐾 operations, what is the maximum possible value of min1≀𝑖≀𝑁𝐴𝑖?

Constraints. 1≀𝑁≀2Γ—105, 1≀𝐴𝑖≀1018, 1≀𝐾≀1018.

Analysis. This problem immediately reminded me of the example problem about maximum median from the binary search section on USACO Guide. The core idea in that problem was to binary search on the answer, and then count how many operations we need. The same technique applies to this problem. Suppose we fix some 𝐿. Then, for each 𝑖, we need

operations to raise 𝐴𝑖 to at least 𝐿 (which should hold for all 𝑖). Therefore, the minimum number of operations needed is βˆ‘π‘–=1𝑁𝐢𝑖, and the answer is at least 𝐿 if and only if this sum is at most 𝐾.

Increasing the minimum can only require more operations, so this subproblem is monotonic in 𝐿. Hence, we binary search on 𝐿 to find the answer.

My code:

val n = nextInt()
val k = nextLong()
val a = LongArray(n) { nextLong() }
var lo = 0L
var hi = Long.MAX_VALUE - 1
while (lo < hi) {
    // thanks 15-122
    val mid = lo + (hi - lo + 1) / 2
    var cost = 0L
    for (i in 0 until n) {
        val diff = mid - a[i]
        if (diff <= 0) continue
        cost += (diff + i) / (i + 1)
        if (cost > k) break
    }
    if (cost > k) hi = mid - 1
    else lo = mid
}
println(lo)

E. Crossing Table Cloth

Problem. You are given 𝑀 segments on the cells 1,2,…,𝑁. For each of the 𝑄 queries, given integers 𝑆≀𝑇, determine whether or not there exist exactly two segments such that their union is exactly [𝑆,𝑇].

Constraints. 1≀𝑁≀2Γ—105, 2≀𝑀≀2Γ—105, 1≀𝑄≀2Γ—105.

Analysis. I immediately noticed that for the answer to be β€œYes,” one of the two segments must have an endpoint at 𝑆 and extends rightwards, and one of the two segments must have an endpoint at 𝑇 and extends leftwards. To maximize the coverage greedily, we should make the former extend as much to the right as possible and make the latter extend as much to the left as possible. This motivates us to maintain a list of segments for each start index and each end index. Then, by sorting each of the lists, we can use binary search to efficiently compute the rightmost-extending segment that starts at 𝑆 and ends before 𝑇, and the leftmost-extending segment that starts after 𝑆 and ends at 𝑇.

There is one edge case where the two aforementioned greedily chosen segments are the same one. In that case, it must be that the segment is already exactly [𝑆,𝑇]. Hence, it suffices to check if there exists another segment lying completely inside [𝑆,𝑇]. This is the same as checking whether there are at least two different segments in [𝑆,𝑇].

I recalled that this subproblem can be solved using a Fenwick Tree: sort the queries by descending order in 𝑆, and maintain a Fenwick Tree on the values of 𝑇.

This problem overall feels very, very natural, as the steps come out one at a time very logically, and the problem reduces to solving a few well-known subproblems one by one using standard techniques, while still having a fun and quick observation.

My code (note that my Fenwick Tree operates on half-open intervals [𝐿,𝑅)):

val n = nextInt()
val m = nextInt()
val start = Array(n + 1) { mutableListOf<Pair<Int, Int>>() }
val end = Array(n + 1) { mutableListOf<Pair<Int, Int>>() }
val intervals = Array(m) {
    val l = nextInt()
    val r = nextInt()
    start[l].add(Pair(r, it))
    end[r].add(Pair(l, it))
    Pair(l, r)
}
// Sort the start and end arrays
for (i in 1..n) {
    start[i].sortWith { x, y ->
        if (x.first == y.first) x.second.compareTo(y.second)
        else x.first.compareTo(y.first)
    }
    end[i].sortWith { x, y ->
        if (x.first == y.first) x.second.compareTo(y.second)
        else x.first.compareTo(y.first)
    }
}
val fw = FenwickTree(n + 1)
val q = nextInt()
// Sort the queries by descending order in S
val queries = Array(q) { Triple(nextInt(), nextInt(), it) }
             .sortedByDescending { it.first }
intervals.sortByDescending { it.first }
// Maintain a pointer to update the Fenwick Tree using two-pointer
var pt = 0
val ans = BooleanArray(q)
for (query in queries) {
    val (s, t, j) = query
    // Add the remaining segments that start after S
    while (pt < m && intervals[pt].first >= s)
        fw.update(intervals[pt++].second, 1)

    var a = start[s].binarySearch { if (it.first > t) 1 else -1 }
    if (a < 0) a = -a - 1
    a--
    var b = end[t].binarySearch { if (it.first >= s) 1 else -1 }
    if (b < 0) b = -b - 1

    if (a < 0 || b >= end[t].size)
        ans[j] = false
    else if (start[s][a].second == end[t][b].second)
        ans[j] = fw.query(t + 1) >= 2L
    else
        ans[j] = start[s][a].first + 1 >= end[t][b].first
}
for (j in 0 until q)
    println(if (ans[j]) "Yes" else "No")

F. Second Gap

Problem. Given an integer array 𝐷 of length π‘βˆ’1, count the number of permutations 𝑃 of 1 through 𝑁, modulo 998244353, such that for each 1β‰€π‘–β‰€π‘βˆ’1, if π‘ƒπ‘Ž and 𝑃𝑏 are the largest and second-largest values among 𝑃𝑖,…,𝑃𝑁, then |π‘Žβˆ’π‘|=𝐷𝑖.

Constraints. 2≀𝑁≀2Γ—105, 1β‰€π·π‘–β‰€π‘βˆ’π‘–.

Analysis. For these types of counting problems, the idea is almost always to use some sort of DP. Furthermore, since the problem is about suffixes, it reminded me that I should consider solving backwards.

I first came up with a naive solution. Let 𝑑𝑝[𝑖][𝑗] denote the number of possible suffixes that start from 𝑖 and has a maximum at index 𝑗.

Fix some 𝑖. Now we can case on whether the largest element or the second-largest element in a suffix appears at position 𝑗.

If neither is true, then the rest of the suffix only contributes a nonzero count from this transition if and only if 𝐷𝑗=𝐷𝑗+1. Hence, if 𝐷𝑗=𝐷𝑗+1, then there are (π‘βˆ’π‘—βˆ’1) choices for the elements, so 𝐷[𝑖][π‘˜]=(π‘βˆ’π‘—βˆ’1)⋅𝐷[𝑖][π‘˜] for all π‘˜>𝑖. If 𝐷𝑗≠𝐷𝑗+1, then set 𝐷[𝑖][π‘˜]=0 for all π‘˜>𝑖.

If the largest element is at position 𝑗, then the second-largest element (which is previously the largest element) must be at position 𝑗+𝐷𝑗, so 𝑑𝑝[𝑖][𝑗]≔𝑑𝑝[𝑖][𝑗]+𝑑𝑝[𝑖+1][𝑗+𝐷𝑗].

If the second-largest element is at position 𝑗, then the largest element (which is also previously the largest element) must be at position 𝑗+𝐷𝑗, so 𝑑𝑝[𝑖][𝑗+𝐷𝑗]≔𝑑𝑝[𝑖][𝑗+𝐷𝑗]+𝑑𝑝[𝑖+1][𝑗+𝐷𝑗].

This is correct but runs in 𝑂(𝑁2), which is not good. The observation is that most of the updates are β€œglobal” (i.e. similar for all the elements). We notice that the transitions are just range multiplication updates and point addition updates, which reminds us of a Lazy Segment Tree. Hence the problem can be solved in 𝑂(𝑁log𝑁) time, which is fast enough.

My code (note that my Segment Tree operates on half-open intervals [𝐿,𝑅)):

val n = nextInt()
val d = IntArray(n - 1) { nextInt() }
val dp = LazySegmentTree(
    n = n + 1,
    e = ModLong(0L),
    id = ModLong(1L),
    defaultValue = ModLong(0L),
    op = { a, b -> a + b },
    mapping = { f, x -> f * x },
    composition = { f, g -> f * g }
)
dp.set(n - 1, ModLong(1L))
dp.set(n, ModLong(1L))
for (i in n - 2 downTo 1) {
    val k = i + d[i - 1]
    val prev = dp.get(k)
    if (d[i] == d[i - 1])
        dp.apply(0, n + 1, ModLong((n - i - 1).toLong()))
    else
        dp.apply(0, n + 1, ModLong(0L))
    dp.set(i, dp.get(i) + prev)
    dp.set(k, dp.get(k) + prev)
}
val ans = dp.query(0, n + 1)
println(ans)

Remark. This problem can also be solved in 𝑂(𝑁), by applying a similar lazy idea and using some modular arithmetic tricks. However, the implementation is uglier. See my submission.

G. Catch All Apples

Problem. 𝑁 apples fall on a number line. Apple 𝑖 reaches coordinate 𝑋𝑖 at time 𝑇𝑖 and must be collected exactly at time 𝑇𝑖. Find the minimum number of robots you need to place so that if the robots have a speed of at most 1 at each moment, there is a way for the robots to collect all 𝑁 apples.

Constraints. 1≀𝑁≀3Γ—105, 𝑇𝑖≀3Γ—105, 𝑋𝑖≀3Γ—105.

Analysis. This is the most fun problem in this set in my opinion. The task feels very β€œrealistic” and natural.

First, I rephrased the problem as follows: what is the minimum number of partitions of the apples we need, such that within each partition, if we sort the apples by increasing 𝑇, then |𝑋𝑖+1βˆ’π‘‹π‘–|≀𝑇𝑖+1βˆ’π‘‡π‘– holds for all 𝑖.

Now, we can apply the sum-difference substitution trick, which states that if π‘Ž>𝑏, then

This can be proven with some simple algebra manipulations. Now suppose 𝑒𝑖=𝑇𝑖+𝑋𝑖 and 𝑣𝑖=π‘‡π‘–βˆ’π‘‹π‘–. Then by above, if π‘‡π‘—βˆ’π‘‡π‘–>0, then |π‘‹π‘—βˆ’π‘‹π‘–|β‰€π‘‡π‘—βˆ’π‘‡π‘–βŸΊπ‘’π‘–β‰€π‘’π‘—βˆ§π‘£π‘–β‰€π‘£π‘—.

Now define the partial relation ≀ on a pair by (π‘Ž,𝑏)≀(𝑐,𝑑) if π‘Žβ‰€π‘βˆ§π‘β‰€π‘‘. We can check that ≀ is reflexive, symmetric, and transitive, so it is an equivalence relation. Hence the (𝑒𝑖,𝑣𝑖) pairs from the apples form a finite partially ordered set, also known as a poset. The problem now reduces to computing the minimum number of chains needed to cover this poset!

We need to recall some Set Theory. By Dilworth’s theorem, the minimum number of chains needed to cover a poset is equal to the maximum length of an antichain. Hence, it suffices to compute the maximum length of an antichain in the pairs (𝑒𝑖,𝑣𝑖).

Note that by definition, (𝑒𝑖,𝑣𝑖),(𝑒𝑗,𝑣𝑗) are incomparable if and only if π‘’π‘–β‰€π‘’π‘—βˆ§π‘£π‘–>𝑣𝑗 or 𝑒𝑖>π‘’π‘—βˆ§π‘£π‘–β‰€π‘£π‘—. If we sort them by 𝑣, then if 𝑖<𝑗, items 𝑖 and 𝑗 are incomparable if and only if 𝑒𝑖>𝑒𝑗. The problems further reduces to the following: given an array π‘ˆ=𝑒1,𝑒2,…,𝑒𝑁, what is the longest subsequence 𝐿 of π‘ˆ such that 𝐿𝑖>𝐿𝑖+1 for all 𝑖?

This is exactly the Longest Decreasing Subsequence (LDS) problem! The solution to this is well-known. For example, we can use dynamic programming with binary search optimization.

To summarize, we transform the apples into pairs (𝑒,𝑣) where 𝑒=𝑇+𝑋 and 𝑣=π‘‡βˆ’π‘‹. After sorting the pairs by 𝑣 in increasing order, the answer is the longest decreasing subsequence in 𝑒 by Dilworth’s theorem.

Since the LIS/LDS problem can be solved in 𝑂(𝑁log𝑁), and the preprocessing is 𝑂(𝑁log𝑁) bottlenecked by sorting, the full solution to this problem runs in 𝑂(𝑁log𝑁), which is fast enough.

My code (here I sorted by 𝑒 and ran LDS on 𝑣, but the same idea works):

val n = nextInt()
// Substitution and sort in the first coordinate
val a = Array(n) {
    val t = nextInt()
    val x = nextInt()
    Pair(t + x, t - x)
}.sortedWith { a, b ->
    if (a.first == b.first)
          a.second.compareTo(b.second)
    else
          a.first.compareTo(b.first)
}
// Compute LDS in the second coordinate
val dp = Array(n + 1) { Int.MIN_VALUE }
dp[0] = Int.MAX_VALUE
var ans = 0
for (i in 0 until n) {
    val v = a[i].second
    var lo = 0
    var hi = ans + 1
    while (hi - lo > 1) {
        val mid = lo + (hi - lo) / 2
        if (dp[mid] <= v)
            hi = mid
        else
            lo = mid
    }
    dp[hi] = v
    if (hi > ans) ans = hi
}
println(ans)