Наиболее эффективный алгоритм для нахождения этого суммирования LCM

Проблема: найти

введите описание изображения здесь

Диапазон 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 (наименьшее простое число), но я не могу понять, как получить ответ, используя факторизацию.

2

Решение

Вы можете попробовать следующий алгоритм:

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
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]