Como puede llegar a 10^{18}, es imposible recorrer los números de Fibonacci uno por uno. El método matricial tampoco sirve: para la suma de las potencias -ésimas haría falta una matriz de tamaño del orden de , y puede llegar a 10^5. Por lo tanto, utilizamos una fórmula explícita para los números de Fibonacci — la fórmula de Binet:
donde
Los números y son las raíces de la ecuación x^2 = x + 1 y, por las fórmulas de Vieta, \varphi\psi = -1.
Desarrollo de la potencia. Elevamos la fórmula de Binet a la potencia y la desarrollamos mediante el binomio de Newton:
Denotemos
Sumamos sobre desde hasta e intercambiamos el orden de la suma:
Así, la suma de términos se reduce a una suma de k + 1 términos.
Progresión geométrica. La suma interior es la suma de una progresión geométrica de razón t = t_j:
El caso t = 1 realmente se presenta y debe tratarse por separado (de lo contrario se produciría una división por cero). Por ejemplo, si es divisible por , entonces para j = k / 2 obtenemos
La potencia t^n se calcula mediante exponenciación binaria en tiempo O(\log n), por lo que cada término se obtiene rápidamente incluso para n = 10^{18}.
Cálculo iterativo de las razones. No es necesario elevar y a una potencia de nuevo para cada . Las razones consecutivas están relacionadas por
por lo que basta con calcular t_0 = \varphi^k una sola vez y luego, en cada iteración, multiplicar el valor actual por .
Cálculos modulares. La aritmética de punto flotante no sirve aquí: necesitamos la respuesta exacta módulo p = 10^9 + 7. Trabajaremos con números de la forma a + b\sqrt{5}, donde y son residuos módulo . La suma y la multiplicación de tales números siguen las reglas
Los números de la forma a + b\sqrt{5} forman el cuerpo \mathbb{Z}_p[\sqrt{5}]. En este cuerpo todo elemento no nulo es invertible:
donde el denominador a^2 - 5b^2 no es cero módulo . Esto garantiza que todas las divisiones de la solución sean correctas. La división por en las fórmulas de y se sustituye por la multiplicación por 2^{-1} \bmod p = (p + 1) / 2.
El inverso de 2. El inverso de módulo es un número tal que 2x \equiv 1 \pmod{p}.
Comprobemos que x = (p + 1) / 2 cumple esta condición:
p = 10^9 + 7 es un número primo impar, por lo que p + 1 es par y (p + 1) / 2 es un número entero. Es menor que , es decir, es un residuo ordinario módulo .
Lo multiplicamos por : 2 \cdot (p + 1) / 2 = p + 1 \equiv 1 \pmod{p}.
Por lo tanto, (p + 1) / 2 es exactamente 2^{-1} \bmod p.
La suma buscada es un número entero, así que en el valor final a + b\sqrt{5} el coeficiente es necesariamente cero y la respuesta es .
Ejemplo
Consideremos el primer ejemplo: , \boldsymbol{k = 1}. Para k = 1 la fórmula toma la forma
Calculemos las potencias de usando la relación \varphi^2 = \varphi + 1:
Su suma es igual a \dfrac{15 + 7\sqrt{5}}{2}. Las potencias de se obtienen sustituyendo \sqrt{5} por -\sqrt{5}, por lo que su suma es igual a \dfrac{15 - 7\sqrt{5}}{2}. Por consiguiente,
lo que coincide con la respuesta: 1 + 1 + 2 + 3 = 7.
Consideremos un ejemplo para \boldsymbol{k = 3}. Desarrollamos el cubo de la diferencia:
Las razones de las progresiones son (usamos \varphi\psi = -1):
Entonces
Cada una de las cuatro sumas se calcula con la fórmula de la progresión geométrica. Comprobémoslo para n = 3. Las potencias de son
Entonces
Las sumas para se obtienen sustituyendo \sqrt{5} por -\sqrt{5}:
Sustituyendo los valores obtenidos:
El resultado coincide con el cálculo directo:
Consideremos un ejemplo para \boldsymbol{k = 4} (caso especial). Aquí
y
El término central corresponde a la razón t_2 = 1: la fórmula de la progresión geométrica no es aplicable y la suma 1 + 1 + \ldots + 1 es igual a . Precisamente este caso se trata por separado en el programa. Por ejemplo, para n = 3 obtenemos:
Implementación del algoritmo
Declaramos una constante para el módulo .
#define MOD 1000000007Guardaremos los valores de los coeficientes binomiales en el arreglo .
vector<long long> Cnk;Para implementar la aritmética en el cuerpo Z_{MOD} [\sqrt{5}], definimos la estructura . Esta estructura representa números de la forma x + y \sqrt{5}.
struct Num
{
long long x, y;
Num(long long x = 0, long long y = 0) : x(x% MOD), y(y% MOD) {}
};La función add implementa la suma en el cuerpo Z_{MOD} [\sqrt{5}].
Num add(const Num& a, const Num& b)
{
return Num((a.x + b.x) % MOD, (a.y + b.y) % MOD);
}La función mult implementa la multiplicación en el cuerpo Z_{MOD} [\sqrt{5}].
Num mult(const Num& a, const Num& b)
{
return Num(
(a.x * b.x + 5 * a.y % MOD * b.y) % MOD,
(a.x * b.y + a.y * b.x) % MOD
);
}La función pw implementa la exponenciación binaria en el cuerpo Z_{MOD} [\sqrt{5}], donde el exponente es un número entero.
Num pw(Num base, long long exp)
{
Num res(1, 0);
while (exp)
{
if (exp & 1) res = mult(res, base);
base = mult(base, base);
exp >>= 1;
}
return res;
}La función pw implementa la exponenciación binaria rápida para calcular a^b módulo .
long long pw(long long a, long long b)
{
long long res = 1;
a %= MOD;
while (b)
{
if (b & 1) res = (res * a) % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}La función inv calcula el inverso multiplicativo de un elemento en el cuerpo Z_{MOD} [\sqrt{5}].
Si
entonces su inverso es
Num inv(const Num& a)
{
long long den = (a.x * a.x - 5 * a.y * a.y) % MOD;
if (den < 0) den += MOD;
long long invDen = pw(den, MOD - 2);
return Num(
a.x * invDen % MOD,
(MOD - a.y) * invDen % MOD
);
}La función divide implementa la división en el cuerpo Z_{MOD} [\sqrt{5}]:
Num divide(const Num& a, const Num& b)
{
return mult(a, inv(b));
}Parte principal del programa. Leemos los datos de entrada.
scanf("%lld %lld", &n, &k);Calculamos los coeficientes binomiales C_k^0, C_k^1, C_k^2, ..., C_k^k y los guardamos en el arreglo . Observemos que
Cnk.resize(k + 1);
Cnk[0] = 1;
for (i = 1; i <= k; i++)
{
Cnk[i] = Cnk[i - 1] * (k - i + 1) % MOD;
Cnk[i] = Cnk[i] * pw(i, MOD - 2) % MOD;
}Calculamos inv2 = 2^{-1} \mod MOD = (MOD + 1) / 2.
long long inv2 = (MOD + 1) / 2;Inicializamos las constantes: phi = \phi = \frac{1 + \sqrt{5}}{2} y psi = \psi = \frac{1 - \sqrt{5}}{2}.
Num phi(inv2, inv2);
Num psi(inv2, MOD - inv2);Calculamos ratio = \psi / \phi.
Num ratio = divide(psi, phi);Inicializamos las constantes y en el cuerpo Z_{MOD} [\sqrt{5}].
Num one(1, 0);
Num minus_one(MOD - 1, 0);La respuesta se acumulará en la variable .
Num res(0, 0);Inicializamos t = \phi^k. Después, en el ciclo, multiplicaremos este valor por , obteniendo
Num t = pw(phi, k);En la variable calculamos la suma
La suma buscada contiene k + 1 términos. Por lo tanto, realizamos k + 1 iteraciones.
for (j = 0; j <= k; j++)
{
Num term;En este momento t = \phi^{k-j} \psi^j. Calculamos la suma de la progresión geométrica:
Sin embargo, el caso t = 1 debe tratarse por separado, ya que la fórmula de la suma de una progresión geométrica contiene una división por . Cuando t = 1 la suma toma la forma 1 + 1 + 1 + ... + 1 ( veces) y es igual a .
if (t.x == 1 && t.y == 0)
term = Num(n % MOD, 0);
else
{
Num tn = pw(t, n);
Num num = add(tn, minus_one); // t^n - 1
term = mult(t, num); // t * (t^n - 1)
Num den = add(t, minus_one); // t - 1
term = divide(term, den); // t * (t^n - 1) / (t - 1)
}Calculamos term = (-1)^j C_k^j \cdot term.
long long coef = Cnk[j];
if (j & 1) coef = MOD - coef;
term.x = term.x * coef % MOD;
term.y = term.y * coef % MOD;Sumamos el siguiente término al resultado .
res = add(res, term);Multiplicamos por y obtenemos t = \phi^{k-j-1} \psi^{j + 1}.
t = mult(t, ratio);
}Queda dividir entre (\sqrt{5})^k.
Num sqrt5(0, 1);
Num sqrt5k = pw(sqrt5, k);
res = divide(res, sqrt5k);Imprimimos la respuesta.
printf("%lld\n", res.x);