Дано натуральное
число 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);
}