Знайти кратне#5621

Знайти кратне (Розбір)

Нехай d_1, d_2, ..., d_n — вхідні числа. Розглянемо всі їх часткові суми

s_i = d_1 + ... + d_i

Оскільки часткових сум рівно , то серед усіх значень s_i~mod~n обов’язково знайдуться або два однакових, або хоча б одне значення, рівне нулю.

Якщо для деяких індексів a - 1 < b виконується

s_{a-1}~mod~n = s_b~mod~n,

то сума d_a + ... + d_b ділиться на .

Отже, послідовність чисел d_a, d_{a + 1}, ..., d_b є шуканою.

Якщо ж існує таке , що s_i~mod~n = 0, то шуканою буде послідовність d_1, d_2, ..., d_i.

Приклад

Розглянемо наведений приклад. Обчислимо всі часткові суми:

Шуканих наборів є декілька. Наприклад:

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

Вхідні числа будемо зберігати в масиві . Масив використовуємо для фіксації часткових сум: якщо s = d_1 + ... + d_i, то присвоїмо r[s] = i.

#define MAX 10100
int d[MAX], r[MAX];

Шуканим набором чисел, сума яких ділиться на , будуть d_a, d_{a+1}, ..., d_b. Виводимо їхню кількість b - a + 1, а потім самі числа — по одному на кожному рядку.

void print(int a, int b)
{
  printf("%d\n",b - a + 1);
  for(int i = a; i <= b; i++)
    printf("%d\n",d[i]);
}

Основна частина програми. Ініціалізуємо масив значенням і встановлюємо r[0] = 0, щоб врахувати випадок, коли сума перших чисел ділиться на .

memset(r,-1,sizeof(r)); r[0] = 0;

Читаємо кількість чисел .

scanf("%d",&n);

Часткові суми за модулем обчислюємо в змінній .

sum = 0;
for (i = 1; i <= n; i++)
{
  scanf("%d", &d[i]);
  sum = (sum + d[i]) % n;

Якщо часткова сума зустрілась повторно, то виводимо шуканий набір чисел:

d_{r[sum] + 1}, d_{r[sum] + 2}, ..., d_i
  if (r[sum] != -1)
  {
    print(r[sum] + 1, i);
    break;
  }

зберігає перший індекс , на якому зустрілася часткова сума d_1 + ... + d_i з залишком при діленні на .

  else r[sum] = i;
}