Непарні дільники#1520

Непарні дільники (Розбір)

Якщо число непарне, то f(n) = n.

Якщо число парне, то f(n) = f(n / 2).

Нехай

g(n) = f(1) + f(2) + ... + f(n)

Розглянемо розбиття множини натуральних чисел від до на два підмножини:

Серед натуральних чисел від до є рівно:

Тоді:

f(1) + f(3) + f(5) + ... + f(2k - 1) = \\ 1 + 3 + 5 + … + (2k - 1) = \\ {1 +2k - 1\over 2} \cdot k = k^2

В той же час:

f(2) + f(4) + f(6) + ... + f(2l) = \\ f(1) + f(2) + f(3) + ... + f(l) = \\ g(l) = g(\left\lfloor n / 2 \right\rfloor)

Для завершення рекурсії покладемо g(0) = 0. Таким чином:

g(n) = \begin{cases} k^2 + g(\left\lfloor n / 2 \right\rfloor), k = (\left\lfloor n + 1) / 2 \right\rfloor \\ 0, n = 0 \end{cases}

Приклад

Розглянемо перший тест, у якому n = 7.

Серед натуральних чисел від до є точно:

g(7) = \left\lfloor 7 + 1 \over 2 \right\rfloor^2 + g\left(\left\lfloor 7 \over 2 \right\rfloor\right) = 16 + g(3) = \\ 16 + \left\lfloor 3 + 1 \over 2 \right\rfloor^2 + g\left(\left\lfloor 3 \over 2 \right\rfloor\right) = 16 + 4 + g(1) = 16 + 4 + 1 = 21

Реалізація алгоритму

Реалізуємо функцію g.

long long g(long long n)
{
  long long k = (n + 1) / 2;
  if (n == 0) return 0;
  return k * k + g(n / 2);
}

Основна частина програми. Для кожного тесту читаємо вхідне значення та виводимо .

while(scanf("%lld",&n) == 1)
  printf("%lld\n",g(n));

Java реалізація

import java.util.*;

public class Main
{
  static long g(long n)
  {
    long k = (n + 1) / 2;
    if (n == 0) return 0;
    return k * k + g(n / 2);
  }
	
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    while(con.hasNext())
    {
      long n = con.nextLong();
      System.out.println(g(n));
    }
  }
}

Python реалізація

Реалізуємо функцію g.

def g(n):
  if n == 0:
    return 0
  k = (n + 1) // 2
  return k * k + g(n // 2)

Основна частина програми. Для кожного тесту читаємо вхідне значення та виводимо .

for line in sys.stdin:
  n = int(line)
  print(g(n))