Sum of Divisors
Pista 1
implica que la complejidad temporal deseada es .
Pista 2
Calcular la suma de los divisores de cada número de a parece imposible: incluso si se pudiera en tiempo constante la solución igual daría TLE.
¿Y si en lugar de partir de los números, partimos de los divisores?
Solución
Explicación
Como se menciona en la Pista 2, partamos de los divisores en lugar de los números.
Si consideramos cualquier divisor , observemos que siempre aparece veces si listamos todos los divisores de los números de a .
Así, para obtener la respuesta solo hay que calcular la siguiente expresión:
Pero calcular esto por fuerza bruta da una solución , que sigue siendo demasiado lenta.
Sin embargo, si listamos los valores de (de ahora en adelante ) para valores de suficientemente grandes, observemos que los valores empiezan a formar “cadenas” largas del mismo valor. Por ejemplo, esta es la lista de valores para :
Llevando este patrón más lejos, se puede mostrar que hay a lo sumo valores distintos de (demostración aquí ).
Sabiendo esto, podemos procesar rápido todos los términos de la sumatoria con el mismo valor de . Empezamos con y saltamos al siguiente valor que produce un distinto en lugar de incrementarlo de forma naive en .
Solución en video
Nota: La solución en video puede no ser la misma que las otras soluciones. Código en C++.
Video de YouTube (JqWiWJQOQyU )
Implementación
Complejidad temporal:
#include <iostream>
using std::cout;
using std::endl;
const int MOD = 1e9 + 7;
const int TWO_MOD_INV = 500000004;
/** @return La suma de todos los números en [start, end] módulo MOD. */
long long total_sum(long long start, long long end) {
return ((((end - start + 1) % MOD) * ((start + end) % MOD) % MOD) * TWO_MOD_INV %
MOD);
}
int main() {
long long n;
std::cin >> n;
long long total = 0;
long long at = 1;
while (at <= n) {
long long add_amt = n / at; // Nuestro divisor a procesar
// El número más grande que todavía tiene el mismo valor de q
long long last_same = n / add_amt;
total = (total + add_amt * total_sum(at, last_same)) % MOD;
at = last_same + 1;
}
cout << total << endl;
}import java.io.*;
public class DivisorSum {
static final int MOD = (int)1e9 + 7;
static final int TWO_MOD_INV = 500000004;
public static void main(String[] args) throws IOException {
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
long n = Long.parseLong(read.readLine());
long total = 0;
long at = 1;
while (at <= n) {
long add_amt = n / at; // Nuestro divisor a procesar
// El número más grande que todavía tiene el mismo valor de q
long last_same = n / add_amt;
total = (total + add_amt * totalSum(at, last_same)) % MOD;
at = last_same + 1;
}
System.out.println(total);
}
/** @return La suma de todos los números en [start, end] módulo MOD. */
static long totalSum(long start, long end) {
return ((((end - start + 1) % MOD) * ((start + end) % MOD) % MOD) *
TWO_MOD_INV % MOD);
}
}MOD = 10**9 + 7
def total_sum(start: int, end: int) -> int:
"""Devuelve la suma de todos los números en [start, end]."""
return (end - start + 1) * (start + end) // 2
n = int(input())
total = 0
at = 1
while at <= n:
add_amt = n // at # Nuestro divisor a procesar
# El número más grande que todavía tiene el mismo valor de q
last_same = n // add_amt
total += add_amt * total_sum(at, last_same)
at = last_same + 1
print(total % MOD)Loan Repayment usa una idea similar.