OMI Training Gate: Sumando sumas
El día de hoy estuve resolviendo este problema y les quiero compartir mi solución y un poco del análisis para poder resolverlo.
Debido a las restricciones del problema, realizar la simulación no es factible, así que habrá que buscar otra forma. Empecemos por analizar el ejemplo de entrada:
4 5 1 6 5 9 14 15 11 26 25 29 54 55 51 106 105 109 214 215 211
Calculemos la suma de cada fila: <pre> 4 + 5 + 1 = 10 6 + 5 + 9 = 20 14 + 15 + 11 = 40 26 + 25 + 29 = 80 54 + 55 + 51 = 160 106+ 105+ 109 = 320 214+ 215+ 211 = 640
¿Notan el patrón? ¿No? Bueno, otro ejemplo:
7 4 5 3
12 15 14 16
45 42 43 41
126 129 128 130 387 384 385 383 1152 1155 1154 1156Y las sumas son: <pre> 7 + 4 + 5 + 3 = 19 12 + 15 + 14 + 16 = 57 45 + 42 + 43 + 41 = 171 126 + 129 + 128 + 130 = 513 387 + 384 + 385 + 383 = 1539 1152 + 1155 + 1154 + 1156 = 4617
Las sumas a partir de la segunda fila corresponden la suma de la fila anterior multiplicada por n − 1, donde n es el número de columnas (el número de vacas que tiene el granjero Juan), en nuestro segundo ejemplo n = 4 y por lo tanto 3 * 19 = 57, 3 * 57 = 171, etc.
Ahora veamos como calculamos el resultado de una de la primera columna:
1539 − 513 + 171 − 57 + 19 − 7 = 1152 513 − 171 + 57 − 19 + 7 = 387
Con un poco de observación llegamos a que el resultado final de una vaca i es igual a:
Donde k = n − 1, s1 es la suma de la primera fila y Ci es el valor inicial de la vaca i.
El problema en estas expresiones es que t es demasiado grande como para iterar y calcular el resultado, intente simplificar el problema pero no llegue a nada, hice una pregunta en math.stackexchange.com y me proporcionaron una formula que en efecto responde a mi pregunta, sin embargo, no pude aplicar esa formula a este problema ya que las propiedades de módulo no se aplican con las divisiones.
Después de investigar llegue a que necesitamos utilizar la siguiente matriz:
Elevamos esta matriz a la potencia t y podremos encontrar el valor que buscamos en M0, 1. Para que la exponenciación funcione en tiempo debemos utilizar el método de exponenciación binaria que tiene una complejidad O(log2n), siendo n el valor del exponente.
Sin más aquí les dejo mi código:
#include <iostream> #include <cassert> #include <cstdio> #include <algorithm> using namespace std; const int N = 50005; const long long mod = 98765431; long long C[N]; class Matrix { public: Matrix() {0; _rows = 0; _cols = } int r, int c) Matrix( { _rows = r; _cols = c; } long long * operator[](int index) {return _m[index]; } operator*(Matrix &B) const Matrix {int i, j, k; Matrix C(rows(), B.cols());for (i = 0; i < rows(); i++) { for (j = 0; j < B.cols(); j++) { 0; C[i][j] = for (k = 0; k < cols(); k++) C[i][j] = (C[i][j] + _m[i][k] * B[k][j]) % mod; } } return C; } operator=(Matrix right) Matrix& { _rows = right.rows(); _cols = right.cols();for (int i = 0; i < right.rows(); i++) for (int j = 0; j < right.cols(); j++) _m[i][j] = right[i][j];return *this; } operator^(int p) const Matrix { p--;this; Matrix a = *this; Matrix b = *while (p > 0) { if (p % 2 == 1) a = a * b;2; p /= b = b * b; } return a; } int cols() const { return _cols; } int rows() const { return _rows; } void print() {for (int i = 0; i < rows(); i++) { "|"); printf(for (int j = 0; j < cols(); j++) "%lld ", _m[i][j]); printf("|\n"); printf( } }private: int _rows, _cols; long long _m[5][5]; }; int main(int argc, char **argv) {long long n, t, sum = 0; int i; "%lld %lld", &n, &t); scanf(for (i = 0; i < n; i++) { "%lld", &C[i]); scanf( sum += C[i]; } sum %= mod; 2, 2); Matrix m(0][0] = -1; m[0][1] = 1; m[1][0] = 0; m[1][1] = n - 1; m[ Matrix b = m^t;long long x = (sum * b[0][1]) % mod; for (i = 0; i < n; i++) { if (t % 2 == 1) { "%lld\n", (x - C[i] + mod) % mod); printf(else { } "%lld\n", (x + C[i]) % mod); printf( } } return 0; }
Hay que tener mucho cuidado con los desbordamientos en los tipos de datos y con valores negativos, en este caso en particular ayuda mucho utilizar un tipo
long long
, sin embargo, esto no es suficiente para todos los casos, es por ellos que hay que realizar la operación módulo en varios puntos.Espero que les sea de utilidad. Hasta la próxima.