Abordagem via Funções Geradoras de Dirichlet
Este problema pode ser resolvido sem utilizar funções geradoras de Dirichlet (DGF), através de combinatória direta. No entanto, a abordagem com DGF oferece uma perspectiva mais elegante e sistemática.
Definição do Operador Derivada
Para uma função aritmética f, definimos sua derivada como:
f'(n) = f(n) × ω(n)
onde ω(n) representa a soma dos expoentes na fatoração prima de n (isto é, o número total de fatores primários com multiplicidade).
Logaritmo e Exponencial de Funções Aritméticas
Utilizando esta definição de derivada, podemos estabelecer as seguintes relações fundamentais:
Logaritmo
A derivada do logaritmo de uma função satisfaz:
ln(F)' = F'/F
Expandindo esta expressão em termos de convolução de Dirichlet:
ln(F)[n] × ω(n) = F[n] × ω(n) - Σ(d|n, d<n) ln(F)[d] × ω(d) × F[n/d]
Exponencial
Para a exponencial, temos:
(e^F)' = e^F × F'
Que se traduz em:
exp(F)[n] × ω(n) = Σ(d|n) exp(F)[d] × F[n/d] × ω(n/d)
Note que após aplicar o logaritmo, temos F[1] = 0, o que implica:
exp(F)[n] × ω(n) = Σ(d|n, d<n) exp(F)[d] × F[n/d] × ω(n/d)
Potenciação k-vezes
Para calcular G = F^k, podemos utilizar as propriedades do logaritmo:
G = F^k
ln(G) = k × ln(F)
G'/G = k × F'/F
G' × F = k × F' × G
Em termos de convolução de Dirichlet:
Σ(d|n) G[d] × ω(d) × F[n/d] = k × Σ(d|n) F[d] × ω(d) × G[n/d]
Considerando que ω(1) = 0, obtemos:
G[n] × ω(n) × F[1] = k × Σ(d|n, d>1) F[d] × ω(d) × G[n/d] - Σ(d|n, d<n) G[d] × ω(d) × F[n/d]
Observação Importante
Assim como em polinômios, ao aplicar as operações de logaritmo e exponencial, o termo constante (valor em 1) deve ser igual a 1. Caso contrário, é necessário normalizar a função antes de realizar estas operações.
Implementação
A seguir, apresentamos uma implementação em C++ que utiliza a abordagem descrita:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MOD = 1000000007;
int composites[MAXN];
int primes[MAXN], primeCount;
int omegaValues[MAXN];
void computePrimes(int limit) {
for (int i = 2; i <= limit; i++) {
if (!composites[i]) {
primes[++primeCount] = i;
omegaValues[i] = 1;
}
for (int j = 1; i * primes[j] <= limit; j++) {
int product = i * primes[j];
composites[product] = 1;
omegaValues[product] = omegaValues[i] + 1;
if (i % primes[j] == 0) break;
}
}
}
long long modPow(long long base, long long exponent) {
long long result = 1;
base %= MOD;
while (exponent) {
if (exponent & 1) result = result * base % MOD;
base = base * base % MOD;
exponent >>= 1;
}
return result;
}
int T, N, K;
vector<int> divisors[MAXN];
long long inputArray[MAXN];
long long functionG[MAXN];
long long auxiliaryH[MAXN];
void processTestCase() {
scanf("%d %d", &N, &K);
for (int i = 1; i <= N; i++) scanf("%lld", &inputArray[i]);
for (int i = 1; i <= N; i++) {
divisors[i].clear();
for (int j = 1; j * i <= N; j++) {
divisors[i * j].push_back(i);
}
}
auxiliaryH[1] = 0;
for (int i = 2; i <= N; i++) {
auxiliaryH[i] = omegaValues[i];
for (int d : divisors[i]) {
if (d < i) {
auxiliaryH[i] = (auxiliaryH[i] - auxiliaryH[d]) % MOD;
}
}
}
for (int i = 1; i <= N; i++) {
auxiliaryH[i] = auxiliaryH[i] * K % MOD;
}
functionG[1] = 1;
for (int i = 2; i <= N; i++) {
functionG[i] = 0;
for (int d : divisors[i]) {
if (d < i) {
functionG[i] = (functionG[i] + functionG[d] * auxiliaryH[i / d]) % MOD;
}
}
functionG[i] = functionG[i] * modPow(omegaValues[i], MOD - 2) % MOD;
}
for (int i = 1; i <= N; i++) {
long long answer = 0;
for (int d : divisors[i]) {
answer = (answer + inputArray[d] * functionG[i / d]) % MOD;
}
answer = (answer % MOD + MOD) % MOD;
printf("%lld ", answer);
}
printf("\n");
}
int main() {
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
computePrimes(100000);
scanf("%d", &T);
for (int test = 1; test <= T; test++) {
processTestCase();
}
return 0;
}
Esta implementação segue o seguinte fluxo:
- Pré-calcular os valores de ω(n) para todos os n até o limite necessário usando o crivo de Eratóstenes
- Para cada caso de teste, construir a lista de divisores para todos os números
- Calcular a função auxiliar h utilizando a fórmula de derivada logarítmica
- Computar a função final g usando exponenciação k-vezes
- Para cada posição i, calcular a resposta como convolução dos valores de entrada com a função g
A complexidade desta solução é O(N log N) por caso de teste, onde N é o valor máximo de n.