Проблема: найти
Диапазон N : 1<= n <знак равно
Основная проблема — обработка запросов (Q), которые могут быть большими. 1 <= Q <знак равно
Методы, которые я использовал до сих пор:
Грубая сила
while(Q--)
{
int N;
cin>>N;
for(int i=1;i<=N;i++)
ans += lcm(i,N)/i ;
}
Предварительная обработка и обработка запросов в
Сначала я строю таблицу, в которой содержится значение функции Эйлера для каждого N.
Это можно сделать за O (N).
void sieve()
{
// phi table holds euler totient function value
// lp holds the lowest prime factor for a number
// pr is a vector which contains prime numbers
phi[1]=1;
for(int i=2;i<=MAX;i++)
{
if(lp[i]==0)
{
lp[i]=i;
phi[i]=i-1;
pr.push_back(i);
}
else
{
if(lp[i]==lp[i/lp[i]])
phi[i] = phi[i/lp[i]]*lp[i];
else phi[i] = phi[i/lp[i]]*(lp[i]-1);
}
for(int j=0;j<(int)pr.size()&&pr[j]<=lp[i]&&i*pr[j]<=MAX;j++)
lp[i*pr[j]] = pr[j];
}
Для каждого запроса разложить N и добавить d*phi[d]
к результату.
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
// i is a factor
sum += (n/i)*phi[n/i];
if(i*i!=n)
{
// n/i is a factor too
sum += i*phi[i];
}
}
}
Это занимает O (sqrt (N)).
Сложность: O (Q * sqrt (N))
Обработка запросов в O (1)
К описанному выше методу сита я добавляю часть, которая вычисляет нужный нам ответ в O (NLogN)
for(int i=1;i<=MAX;++i)
{
//MAX is 10^7
for(int j=i;j<=MAX;j+=i)
{
ans[j] += i*phi[i];
}
}
Это, к сожалению, истекает для заданных ограничений и ограничения по времени (1 секунда).
Я думаю, что это включает в себя некоторую умную идею относительно факторизации N.
Я могу просторизовать число в O (LogN), используя построенную выше таблицу lp (наименьшее простое число), но я не могу понять, как получить ответ, используя факторизацию.
Вы можете попробовать следующий алгоритм:
lcm(i,n) / i = i * n / i * gcd(i, n) = n / gcd(i, n)
Теперь следует найти сумму чисел n / gcd(i, n)
,
Давайте n = p1^i1 * p2^i2 * p3^j3
где номер p1, p2, ... pk
прост.
Количество предметов n / gdc(i, n)
где gcd(i , n) == 1
является phi[n] = n*(p1-1)*(p2-1)*...*(pk-1)/(p1*p2*...*pk)
, так что прибавьте к сумме n*phi[n]
,
Количество предметов n / gdc(i, n)
где gcd(i , n) == p1
является phi[n/p1] = (n/p1)*(p1-1)*(p2-1)*...*(pk-1)/(p1*p2*...*pk)
, так что прибавьте к сумме n/p1*phi[n/p1]
,
Количество предметов n / gdc(i, n)
где gcd(i , n) == p1*p2
является phi[n/(p1*p2)] = (n/(p1*p2))*(p1-1)*(p2-1)*...*(pk-1)/(p1*p2*...*pk)
, так что прибавьте к сумме n/(p1*p2)*phi[n/(p1*p2)]
,
Теперь ответ — сумма
n/(p1^j1*p2^j2*...*pk^jk) phi[n/(p1^j1*p2^j2*...*pk^jk)]
в общем и целом
j1=0,...,i1
j2=0,...,i2
....
jk=0,...,ik
Общее количество предметов в этой сумме составляет i1*i2*...*ik
это значительно меньше, чем O (n).
Для вычисления этой суммы вы можете использовать рекурсивную функцию с начальным номером свободного аргумента, текущим представлением и начальным представлением:
initial = {p1:i1, p2:i2, ... ,pn:in}
current = {p1:i1, p2:i2, ... ,pn:in}
visited = {}
int calc(n, initial, current, visited):
if(current in visited):
return 0
visited add current
int sum = 0
for pj in keys of current:
if current[pj] == 0:
continue
current[pj]--
sum += calc(n, initial, current)
current[pj]++
mult1 = n
for pj in keys of current:
mult1 /= pj^current[pj]
mult2 = mult1
for pj in keys of current:
if initial[pj] == current[pj]:
continue
mult2 = mult2*(pj -1)/pj
sum += milt1 * mult2
return sum
Других решений пока нет …