1530. Расширенное счастливое число

 

Дано натуральное число n. Возведем в k - ую степень каждую его цифру и просуммируем полученные результаты. Обозначим результат через Sk(n). Например, S2(65) = 62 + 52 = 61. Построим последовательность n, Sk(n), Sk(Sk(n)), … .

Счастьем числа n по отношению к k будем называть наименьшее число в этой последовательности.

 

Вход.  Каждая строка является отдельным тестом и содержит три целых числа a, b (1 ≤ a, b ≤ 106) и k (1 ≤ k ≤ 6).

 

Выход. Для каждого теста вычислить счастье каждого числа от a до b включительно по отношению к k и вывести их сумму.

 

Пример входа

Пример выхода

13 13 2

1 5 2

535 538 3

1

14

820

 

 

РЕШЕНИЕ

динамическое программирование

 

Анализ алгоритма

Поскольку значение k в процессе вычислений фиксировано, заведем массив powk, в котором powk[i] = ik. Им будет удобно пользоваться при вычислении значений Sk(n) в функции sum(n). Наибольшее значение, которое могут принимать элементы последовательности n, Sk(n), Sk(Sk(n)), …, не больше 4*106, так как даже при k = 6 значение S6(n) при n <  4*106 не больше 36 + 6*96 = 3189375 < 4*106.

Наивное решение задачи состоит в том, чтобы для каждого значения i (a i b) строить последовательность i, Sk(i), Sk(Sk(i)), … до тех пор, пока некоторое число не повторится в ней дважды. После этого в последовательности уже не будут появляться новые числа (числа будут повторяться). Остается найти наименьшее значение среди построенных чисел. Однако такое решение работает достаточно долго.

Заведем массив f[4000000], в котором будем хранить f[i] = Sk(i). Рассмотрим для некоторого i последовательность a0 = i, a1 = Sk(i), a2 = Sk(Sk(n)), … . Очевидно, что найдутся такие индексы s и t (пусть s < t для определенности), что as = at.

 

 

Теперь значение f[ak], (0 £ k £ t) положим равным минимуму среди чисел ak, ak+1, …, at. При этом начиная вычислять значение f[a0], в процессе мы находим все значения f[ak], 0 £ k £ t.

 

 

Описанную процедуру совершаем в функции g. Значение f[i] считается вычисленным, если f[i] > 0. При первом проходе по числам a0, …, at устанавливаем f[ai] = -1. Дойдя до at = as, продолжаем путь as , as+1, …, at, устанавливая f[ai] = ai для s £ i £ t. Когда вторично дойдем до f[at], значение этой ячейки уже будет положительным и рекурсия начнет раскручиваться назад. Двигаясь назад по кольцу дважды от at до as, а потом и до a0, устанавливаем f[ai] в наименьшее значение среди чисел ai, ai+1, …, at.

 

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

Объявим необходимые массивы и переменные.

 

#define MAX 5000001

int powk[10], f[MAX], ptr;

long long res;

 

Функция sum вычисляет значение Sk(n), равное сумме k - ых степеней всех его цифр.

 

int sum(int n)

{

  int s;

  for(s = 0; n; n /= 10)

    s += powk[n % 10];

  return s;

}

 

int g(int n)

{

 

Если значение f[n] уже вычислено, то возвращаем его.

 

  if (f[n] > 0) return f[n]; else

 

При первом проходе по числам a0, …, as, …, at = as устанавливаем f[ai] = -1.

 

  if (f[n] == 0) f[n] = -1; else

 

Сюда попадаем, когда дойдем до as второй раз (часть пути от a0 до as и полный круг от as до as).

 

  if (f[n] == -1) f[n] = n;

  return f[n] = min(n,g(sum(n)));

}

 

Функция calcTheSum вычисляет сумму счастья всех чисел от a до b включительно по отношению к k.

 

long long calcTheSum(int a, int b, int k)

{

  int i, j, s;

  long long res = 0;

  for(i = 0; i < 10; i++)

  {

    for(s = 1, j = 0; j < k; j++) s *= i;

    powk[i] = s;

  }

  for(i = a; i <= b; i++)

    res += g(i);

  return res;

}

 

Основная часть программы.

 

while(scanf("%d %d %d",&a,&b,&k) == 3)

{

  memset(f,0,sizeof(f));

  res = calcTheSum(a,b,k);

  printf("%lld\n",res);

}