Skip to Content

Cow Camp

Análisis oficial (Python, Java) 

Solución

Podemos restar 11 de TT porque siempre obtenemos el caso de prueba de ejemplo y lo sumamos a nuestra respuesta final.

Sea ExE_x el valor esperado después de a lo sumo xx envíos. Buscamos EKE_K. Para obtener el valor esperado del envío final, podemos hallar la relación entre ExE_x y Ex1E_{x - 1}.

Para esto podemos expresar ExE_x en términos de Ex1E_{x -1} y pip_i, que es la probabilidad de obtener exactamente ii casos de prueba.

Después de x1x-1 envíos, se espera obtener Ex1E_{x-1} casos de prueba. Hay tres posibilidades si se decide reenviar.

Caso 1: Si se obtienen menos casos de prueba que Ex1E_{x-1}, no se quiere reenviar.

Caso 2: Si se obtienen más casos de prueba que Ex1E_{x-1}, sí se quiere reenviar.

Caso 3: Si se obtiene el mismo número de casos de prueba que Ex1E_{x-1}, no importa si se reenvía. En este caso no perjudica reenviar o quedarse igual, así que podemos combinarlo con el Caso 1 (o el Caso 2).

La probabilidad de obtener a lo sumo Ex1E_{x-1} casos de prueba es i=0Ex1pi{\sum_{i=0}}^{E_{x - 1}} p_i. Podemos multiplicar esto por Ex1E_{x-1} para obtener el valor esperado de ExE_x si nos detenemos antes del xx-ésimo envío y obtenemos Ex1i=0Ex1piE_{x-1} \cdot {\sum_{i=0}}^{E_{x - 1}} p_i

La probabilidad de obtener más de Ex1E_{x-1} casos de prueba es i=Ex1+1Tpi{\sum_{i=\lfloor E_{x-1} \rfloor + 1}}^T p_i. El valor esperado de obtener más de Ex1E_{x-1} casos de prueba es la suma de ii desde Ex1+1{\lfloor E_{x-1} \rfloor + 1} hasta TT de la probabilidad de obtener ii casos de prueba multiplicada por ii, o Ex1+1Tpii{\sum_{\lfloor E_{x-1} \rfloor + 1}}^T p_i i.

Además, estas fórmulas para ambos casos pueden ayudarnos a demostrar matemáticamente que se puede fusionar el Caso 3 con el Caso 1 o el 2. La única diferencia entre ambos casos es si i=Ex1i = E_{x-1} pertenece a la primera o a la segunda suma. Si lo fusionamos con el Caso 1, el término de la suma será Ex1pEx1E_{x-1} \cdot p_{E_{x-1}}. Si lo fusionamos con el Caso 3, también será Ex1pEx1E_{x-1} \cdot p_{E_{x-1}}. Esto ocurre porque piEx1=piip_i \cdot E_{x-1} = p_i \cdot i solo se cumple cuando i=Ex1i=E_{x-1}.

Sumar las dos partes da nuestra expresión final para el valor esperado:

Ex=Ex1i=0Ex1pi+i=Ex1+1TipiE_x = E_{x -1} \cdot \sum_{i=0}^{\lfloor E_{x - 1} \rfloor} p_i + \sum_{i=\lfloor E_{x - 1} + 1 \rfloor}^{T} i p_i

Podemos resolver el problema simulando cada caso de prueba y recalculando las sumas para cada caso de prueba en O(TK)O(TK). Sin embargo, esto se puede acelerar fácilmente a O(T2+K)O(T^2 + K) precalculando pip_i usando la identidad de Pascal y precalculando las sumas de prefijos tanto de pip_i como de piip_i i para obtener los valores de las sumas en O(1)O(1) en lugar de O(T)O(T). Esto nos da los primeros 99 casos de prueba pero no es suficiente.

Para resolver el problema por completo, necesitamos quitar el factor O(K)O(K). Podemos hacerlo si somos capaces de procesar varios valores de ExE_x en un solo paso.

Afirmación: Podemos procesar varias consultas si los valores de i=0Ex1pi{\sum_{i=0}}^{\lfloor E_{x - 1} \rfloor} p_i y i=Ex1+1Tipi{\sum_{i=\lfloor E_{x - 1} + 1 \rfloor}}^{T} i p_i no cambian entre consultas.

Demostración: Sea a=i=0Ex1pia = {\sum_{i=0}}^{\lfloor E_{x - 1} \rfloor} p_i y b=i=Ex1+1Tipib = {\sum_{i=\lfloor E_{x - 1} + 1 \rfloor}}^{T} i p_i. Entonces nuestra fórmula queda

Ex=Ex1a+bE_x = E_{x - 1} \cdot a + b

Como aa y bb son constantes, podemos extender esta fórmula y obtener

Ex=a(a...(aExk+b)...+b)+bE_x = a \cdot (a \cdot ... (a \cdot E_{x - k} + b) ... + b) + b

Simplificando obtenemos una sucesión geométrica.

Ex=akExk+ak1b+ak2+...+bE_x = a^k \cdot E_{x - k} + a^{k - 1}b + a^{k - 2} + ... + b

Podemos reescribir del segundo al último término usando la fórmula de una serie geométrica.

Ex=akExk+b(ak1)a1E_x = a^k \cdot E_{x - k} + \frac{b(a^k - 1)}{a - 1}

Aquí hay más información sobre series geométricas .

Sin embargo, los valores de aa y bb no siempre permanecerán iguales. Podemos hacer búsqueda binaria sobre el número máximo de envíos que Bessie puede hacer antes de que cambien los valores de aa y bb. aa y bb cambiarán cuando cambie el valor de Ex{\lfloor E_x \rfloor}. Las condiciones de la búsqueda binaria se cumplen porque ExE_x es una función no decreciente.

Como hay a lo sumo TT valores de ExE_x, aa y bb pueden cambiar a lo sumo TT veces, así que la complejidad temporal resultante será O(T2+TlogK)O(T^2 + T \log K).

Implementación

Complejidad temporal: O(T2+TlogK)\mathcal{O}(T^2 + T \log K)

#include <bits/stdc++.h> using namespace std; using ll = long long; using db = double; const int MAXT = 1000; db prob[MAXT + 1][MAXT + 1]; db pref_prob[MAXT + 1]; db pref_exp[MAXT + 1]; // Función para saltar x envíos con constantes a y b y el valor // esperado actual igual a E. db skip_submissions(db a, db b, db E, ll x) { return pow(a, x) * E + b * (pow(a, x) - 1) / (a - 1); } int main() { int T; ll K; cin >> T >> K; // Restamos el caso de prueba de ejemplo T--; // Precomputamos la probabilidad de elegir exactamente i casos de prueba usando // la identidad de Pascal. prob[0][0] = 1; for (int i = 1; i <= T; i++) { prob[i][0] = prob[i - 1][0] / 2; for (int j = 1; j <= T; j++) prob[i][j] = (prob[i - 1][j] + prob[i - 1][j - 1]) / 2; } // Creamos suma de prefijos de probabilidad pref_prob[0] = prob[T][0]; for (int i = 1; i <= T; i++) pref_prob[i] = pref_prob[i - 1] + prob[T][i]; // Creamos suma de prefijos de valor esperado pref_exp[0] = 0; for (int i = 1; i <= T; i++) pref_exp[i] = pref_exp[i - 1] + prob[T][i] * i; db E = 0; while (K != 0) { /* * Hallamos el piso del valor esperado actual * que actúa como el límite de cuántos envíos podemos saltar hacia adelante. */ ll cross = (ll)floor(E); // Hallamos las constantes a y b db a = pref_prob[cross], b = pref_exp[T] - pref_exp[cross]; // Búsqueda binaria para hallar cuántos envíos podemos saltar ll lo = 1; ll hi = 1e9; while (lo < hi) { ll mid = (lo + hi + 1) / 2; // Revisamos si 'mid' envíos cruzan el límite 'cross'. if (floor(skip_submissions(a, b, E, mid)) == cross) { lo = mid; } else { hi = mid - 1; } } /* * Saltamos 'lo' envíos o la cantidad restante de envíos * si quedan menos de 'lo' envíos. */ lo = min(lo, K); E = skip_submissions(a, b, E, lo); K -= lo; } cout << fixed << setprecision(20); cout << E + 1 << endl; }
import math def skip_submissions(a: float, b: float, e: float, x: float) -> float: """ Función que salta x envíos hacia adelante con constantes a y b y valor esperado actual e según la fórmula de la serie geométrica """ return pow(a, x) * e + b * (pow(a, x) - 1) / (a - 1) t, k = map(int, input().split()) # Restamos el ejemplo t -= 1 # Precomputamos la probabilidad de obtener i casos de prueba usando la identidad de Pascal prob = [[0 for i in range(t + 1)] for j in range(t + 1)] prob[0][0] = 1 for i in range(1, t + 1): prob[i][0] = prob[i - 1][0] / 2 for j in range(1, t + 1): prob[i][j] = (prob[i - 1][j] + prob[i - 1][j - 1]) / 2 # Sumas de prefijos de probabilidad pref_prob = [0 for i in range(t + 1)] pref_prob[0] = prob[t][0] for i in range(1, t + 1): pref_prob[i] = pref_prob[i - 1] + prob[t][i] # Sumas de prefijos de valor esperado pref_exp = [0 for i in range(t + 1)] pref_exp[0] = 0 for i in range(1, t + 1): pref_exp[i] = pref_exp[i - 1] + prob[t][i] * i e = 0 while k != 0: """ Hallamos el piso del valor esperado actual que también es el límite de cuántos envíos podemos saltar hacia adelante """ cross = math.floor(e) # Hallamos a y b usando las sumas de prefijos precomputadas a = pref_prob[cross] b = pref_exp[t] - pref_exp[cross] # Búsqueda binaria para ver cuántos envíos podemos saltar lo = 1 hi = 10**9 while lo < hi: mid = math.floor((lo + hi + 1) / 2) if math.floor(skip_submissions(a, b, e, mid)) == cross: lo = mid else: hi = mid - 1 """ Saltamos lo envíos hacia adelante Si el número restante de envíos es menor que lo, saltamos los envíos restantes """ lo = min(lo, k) e = skip_submissions(a, b, e, lo) k -= lo print(e + 1)