Read before:
According to what it seemed previously, the practice stage of Kotlin Heroes series contests may never be followed by official editorials and can not be submitted after it's finished. This blog aims to be an archive of problems, my corresponding ideas as well as submissions and some templates for faster Kotlin code editing later on.
All code blocks in this blog can be compiled with Kotlin only due to the contest's regulations.
Ahoj, Codeforces!
I had been absent from Codeforces along with Kotlin-only rounds for over one year thanks to my own business, but I kept waiting for the practice stage's initial return this year as I turn available for codings — the waiting eventually got paid off when Kotlin Heroes: Practice 12 was back with the relieving news to be held on Mar/31/2025 16:35 (Moscow time) and last for 7 days.
This blog is to share thoughts on Kotlin the language and to archive problems as well as solutions for the contest. I hope this will help readers obtain a better understanding of the designed-for-Android programming language.
To prepare for faster coding when using Kotlin the language, templates may be employed (just like what participants have done in other contests).
import java.util.*
private val OUTPUT = System.out!!
private const val yes = "YES"
private const val no = "NO"
private val INPUT = System.`in`
private val reader = INPUT.bufferedReader()
private const val BUFFER_SIZE = 1 shl 16
private val buffer = ByteArray(BUFFER_SIZE)
private var bufferPt = 0
private var bytesRead = 0
private var st: StringTokenizer = StringTokenizer("")
private fun read(): String {
while (st.hasMoreTokens().not())
st = StringTokenizer(reader.readLine() ?: return "", " ")
return st.nextToken()
}
private tailrec fun readChar(): Char {
if (bufferPt == bytesRead) {
bufferPt = 0
bytesRead = INPUT.read(buffer, 0, BUFFER_SIZE)
}
return if (bytesRead < 0) Char.MIN_VALUE
else {
val c = buffer[bufferPt++].toInt().toChar()
if (c == '\r') readChar()
else c
}
}
private fun readInt() = read().toInt()
private fun readDouble() = read().toDouble()
private fun readLong() = read().toLong()
private fun readStrings(n: Int) = List(n) { read() }
private fun readLines(n: Int) = List(n) { readln() }
private fun readInts(n: Int) = List(n) { read().toInt() } // val (x,y,z) = readInts(3)
private fun readIntArray(n: Int) = IntArray(n) { read().toInt() } // val arr = readIntArray(3)
private fun readDoubles(n: Int) = List(n) { read().toDouble() }
private fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
private fun readLongs(n: Int) = List(n) { read().toLong() }
private fun readLongArray(n: Int) = LongArray(n) { read().toLong() }
private fun printWithSpace(vararg x: Any?) = println(x.joinToString(" ")) // printWithSpace(a, b, c)
private fun readCharArray(n: Int) = CharArray(n) { _ -> readChar() }
/* Customizing functions special for problems */
/* End of customized functions */
private fun solve() {
/* Solution here */
/* End of solution */
}
fun main() {
val t = readInt() // Codeforces problems special
repeat(t) { solve() }
}
}
All code blocks as solutions below will be based on the template mentioned above.
2088A - Easy Problem
According to the problem, both $$$a$$$ and $$$b$$$ will only range from $$$1$$$ to $$$n-1$$$ due to the fact that they are all positive integers and $$$a=n-b$$$.
We also know that for each of $$$a$$$s ascending from $$$1$$$ to $$$n-1$$$ will find a $$$b$$$ descending from $$$n-1$$$ to $$$1$$$ as a match, so there are always $$$n-1$$$ pairs of positive integers that satisfies.
It's the easiest problem I've ever come across in Codeforces.
private fun solve() {
val n = readInt()
println(n - 1)
}
2088B - Having Been a Treasurer in the Past, I Help Goblins Deceive
The over-length description of the problem can be translated into the following sentence —
Given a string $$$s$$$ that only contains "-" and "_", try to rearrange the string to obtain the maximum number of subsequences "-_-".
Let's focus on the mouth (_). Suppose there are $$$L$$$ eyeball signs (-) on the left side of a specific mouth sign and $$$R$$$ on the other, there must be $$$L\times R$$$ subsequences "-_-" that use the specific mouth sign. We know that the total number of eyeball signs is fixed (let's mark it as $$$a$$$), so the number of subsequences that satisfy will be $$$x(a-x)$$$ for one mouth if there are $$$x$$$ eyeball signs on the one side of it.
Therefore, to make the number of correct subsequences to the maximum for every mouth signs, just concentrate all of them at the mid of the rearranged string (because of the maximum that the quadratic function $$$f(x)=x(a-x)$$$ can reach will take place at $$$x=\frac{a}{2}$$$). So if there are $$$b$$$ mouths in $$$s$$$, the result will be
Note that $$$n$$$ can reach $$$2\times 10^5$$$, which means the result can go beyond the limitations of Int.
private fun solve() {
val n = readInt()
val s = readStrings(1)
if (n < 3) {
println(0)
return
}
var eyeballCount = 0L
var mouthCount = 0L
for (i in s[0]) {
when (i) {
'_' -> mouthCount++
else -> eyeballCount++
}
}
if (eyeballCount < 2 || mouthCount == 0L) {
println(0)
return
}
println(
when ((eyeballCount % 2).toInt() == 0) {
true -> mouthCount * (eyeballCount / 2) * (eyeballCount / 2)
else -> mouthCount * ((eyeballCount - 1) / 2) * ((eyeballCount + 1) / 2)
}
)
}
2088C - Farmer John's Card Game
The over-length description can be summarized as the following 2 sentences —
Check whether the cow that got the smallest/smaller/larger/largest card in the previous round still has the smallest/smaller/larger/largest card in this round and will still have the smallest/smaller/larger/largest card in the next rounds;
Check whether the cow who got the smallest cards is capable of standing challenges from the largest card from the last round.
It is significant that the cow who got the card $$$0$$$ should be the first to take out his card — there's no way for the poor cow to give this card out later on otherwise, making the game's loss inevitable. Similarly, only when the number on the card on pile increase by only $$$1$$$ can the cows win the game (owing to the fact that numbers on cards only vary from $$$0$$$ to $$$mn-1$$$).
Just ask the cows to sort their own cards at first — they have to take cards out in an ascending order. Then just check whether they can pile up cards via keep the number on the top increasing by $$$1$$$ each step in every round — there must be at least one card remaining otherwise, so it's not necessary to make comparisons between the first card in a round and the last card in the previous.
private fun solve() {
val (n, m) = readInts(2)
val cards = Array(n) { Array<Int>(m) { readInt() } }
for(i in 0..<n){
cards[i].sort()
}
val cowNumbers = Array(n) { i -> Array<Int>(1) { i + 1 } }
val cowMap = Array(n) { j ->
Array<Int>(m + 1) { i ->
when (i) {
0 -> cowNumbers[j][i]
else -> cards[j][i - 1]
}
}
}
cowMap.sortBy { it[1] }
for (i in 2..m) {
for (j in 1..<n) {
if (cowMap[j][i] == cowMap[j - 1][i] + 1)
continue
else {
println(-1)
return
}
}
}
for (i in 0..<n) {
print("${cowMap[i][0]} ")
}
println()
}
If you are wondering why if (cowMap[j][i] == cowMap[j - 1][i] + 1) continue cannot be changed to if (cowMap[j][i] > cowMap[j - 1][i]) continue, just think about the following test case ($$$t=1$$$) for solve() the function —
2 4
0 2 4 5
1 3 6 7
Output will be 1 2 if the condition's changed to the latter version, but it actually should be -1. This is because the former condition takes potential account of comparisons of the smallest card of this round and the largest card of the previous round, but the latter one doesn't.
Section determining whether the card distribution is OK can be simplified by direct calculations on whether the card one have got (sorted) equals to expected, according to ideas mentioned above.
2088D - Counting Pairs
Suppose the sum of the initial array $$$a$$$ to be $$$\sigma$$$ — we are just to find the number of pairs $$$(i,j)$$$ that $$$\sigma-y\le a_i+a_j\le\sigma-x$$$.
Brute force, beyond no doubt, will feature the time complexity of $$$O(n^2)$$$, which has proved time limit exceeded by submission 313307531. Think about a faster method — we can sort the array and, for each of the elements in the new array $$$a'_i$$$, we can find the index $$$j$$$ of the first element $$$a'_j$$$ that is greater than $$$\sigma-y-a'_i$$$, as well as that of the last element $$$a'_k$$$ that is less than $$$\sigma-x-a'_i$$$ (Both $$$j$$$ and $$$k$$$ should be greater than $$$i$$$). The number of pairs that meet what the problem demands and contain $$$a'_i$$$ will be $$$k-j+1$$$. It will be faster if we choose binary search for $$$j$$$ and $$$k$$$.
Think about why we can sort the array. Despite the fact that the problem emphasizes that $$$i \lt j$$$, it only means that $$$(i,j)$$$ and $$$(j,i)$$$ should be counted just once in total — if for $$$i \lt j$$$, $$$a_i \gt a_j$$$, it will be counted as just $$$(i,j)$$$ originally but just $$$(j,i)$$$ after sorting. No additional counts will be conducted.
private fun findFirstGE(a: LongArray, origin: Int, dest: Int, x: Long): Int {
var left = origin
var right = dest
var result = dest + 1
while (left <= right) {
val mid = (left + right) / 2
if (a[mid] >= x) {
result = mid
right = mid - 1
} else left = mid + 1
}
return result
}
private fun findLastLE(a: LongArray, origin: Int, dest: Int, x: Long): Int {
var left = origin
var right = dest
var result = -1
while (left <= right) {
val mid = (left + right) / 2
if (a[mid] <= x) {
result = mid
left = mid + 1
} else right = mid - 1
}
return result
}
private fun solve() {
val n = readInt()
val (x, y) = readLongs(2)
val a = readLongArray(n)
a.sort()
var sum = 0L
for (i in a) {
sum += i
}
val minDecline = sum - y
val maxDecline = sum - x
var ans = 0L
for (i in 0..<n - 1) {
val left = findFirstGE(a, i + 1, n - 1, minDecline - a[i])
val right = findLastLE(a, i + 1, n - 1, maxDecline - a[i])
if (left <= right) ans += right - left + 1
}
println(ans)
}
2088E - Doggo Recoloring
Beyond no doubt, it has been standardized already if there is only one single dog.
For a row of several dogs, we should have at least 2 dogs with the same color to make paintings. Suppose there have been at least 2 dogs in color $$$x$$$, we can paint them in another color $$$y$$$ and afterwards, the number of dogs in the color $$$y$$$ will be more than 2, ready for further repaints.
Therefore, as soon as there is a color of which the number of dogs is more than $$$1$$$, the color can be a launcher of repaint and all other colors can be reached in later operations.
private fun solve() {
val n = readInt()
val s = readStrings(1)[0]
if (n == 1) {
println(yes)
return
}
val colorCount = IntArray(26) { 0 }
for (i in s) {
if (++colorCount[i - 'a'] >= 2) {
println(yes)
return
}
}
println(no)
}
(Due to other affairs for job-searching near the end of the contest, I failed to solve the rest 2 problems on time.)
From the first 5 problems solved by me myself, it can be easily found out that Kotlin normally features easy-to-read code blocks, making readers easy to know what each of the steps stands for and functions as, but it's not the case for multidimensional-array-related builds.







