https://coderun.yandex.ru/problem/find-rle-string-length Средняя

Решение

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter

data class Run(val letter: Char, val count: Long, val start: Long, val end: Long)

fun main(args: Array<String>) {
    val reader = BufferedReader(InputStreamReader(System.`in`))
    val writer = BufferedWriter(OutputStreamWriter(System.out))
    
    // Считываем RLE-сжатую строку s
    val s = reader.readLine()
    val runs = ArrayList<Run>()
    var i = 0
    var pos = 1L  // позиции в исходной строке t (1-indexed)
    while (i < s.length) {
        var cnt: Long = 1
        // Если символ — цифра, читаем число
        if (s[i].isDigit()) {
            var num = 0L
            while (i < s.length && s[i].isDigit()) {
                num = num * 10 + (s[i] - '0')
                i++
            }
            cnt = num
        }
        // Далее обязательно должна идти буква
        val letter = s[i]
        i++
        val start = pos
        val end = pos + cnt - 1
        runs.add(Run(letter, cnt, start, end))
        pos = end + 1
    }
    val totalLength = pos - 1  // длина строки t
    
    // Создадим массив концов запусков (для бинарного поиска)
    val nRuns = runs.size
    val ends = LongArray(nRuns)
    val starts = LongArray(nRuns)
    for (j in 0 until nRuns) {
        ends[j] = runs[j].end
        starts[j] = runs[j].start
    }
    
    // Функция подсчёта количества цифр у числа (при count>=1)
    fun digits(x: Long): Int {
        var cnt = 0
        var y = x
        while (y > 0) {
            cnt++
            y /= 10
        }
        return cnt
    }
    // Функция вычисления длины RLE-токена для заданного количества повторов
    fun tokenLen(cnt: Long): Int {
        return if (cnt == 1L) 1 else digits(cnt) + 1
    }
    
    // Предвычислим для каждого полного запуска его RLE-длину и построим префиксный массив
    val tokenLens = IntArray(nRuns)
    for (j in 0 until nRuns) {
        tokenLens[j] = tokenLen(runs[j].count)
    }
    // Префиксная сумма: pre[0] = 0, pre[i+1] = pre[i] + tokenLens[i]
    val pre = IntArray(nRuns + 1)
    for (j in 0 until nRuns) {
        pre[j + 1] = pre[j] + tokenLens[j]
    }
    
    // Функция бинарного поиска: находим минимальный индекс i, что ends[i] >= pos
    fun findRun(posInT: Long): Int {
        var lo = 0
        var hi = nRuns - 1
        while (lo < hi) {
            val mid = (lo + hi) / 2
            if (ends[mid] >= posInT) {
                hi = mid
            } else {
                lo = mid + 1
            }
        }
        return lo
    }
    
    // Читаем запросы
    val q = reader.readLine().toInt()
    val output = StringBuilder()
    repeat(q) {
        val parts = reader.readLine().split(" ")
        val l = parts[0].toLong()
        val r = parts[1].toLong()
        // Найдем индексы запусков, содержащих позиции l и r
        val Lidx = findRun(l)
        val Ridx = findRun(r)
        val ans: Int
        if (Lidx == Ridx) {
            // Запрос полностью внутри одного запуска
            val segLen = (r - l + 1)
            ans = if (segLen == 1L) 1 else digits(segLen) + 1
        } else {
            // Первый токен: из запуска Lidx – от l до runs[Lidx].end
            val partA = runs[Lidx].end - l + 1
            val tokenA = if (partA == 1L) 1 else digits(partA) + 1
            // Последний токен: из запуска Ridx – от runs[Ridx].start до r
            val partC = r - runs[Ridx].start + 1
            val tokenC = if (partC == 1L) 1 else digits(partC) + 1
            // Для полных запусков между ними (если есть)
            val midSum = if (Ridx - Lidx - 1 > 0) pre[Ridx] - pre[Lidx + 1] else 0
            ans = tokenA + tokenC + midSum
        }
        output.append(ans).append("\\n")
    }
    
    writer.write(output.toString())
    writer.flush()
    writer.close()
    reader.close()
}