Зустріч посередині#11431

Зустріч посередині (Розбір)

Розіб'ємо вхідний масив на дві рівні частини: t_1, t_2, ..., t_{n/2} і t_{n/2+1}, t_{n/2+2}, ..., t_n. Першу частину збережемо в масиві , другу — в масиві .

Спочатку вирішимо задачу для послідовності . Переберемо всі її можливі підмножини (їх буде не більше 2^{20}). Для кожної підмножини обчислимо суму її елементів , і для кожної такої суми збільшимо значення на . Як структуру даних можна використовувати, наприклад, unordered_map. Таким чином, буде містити кількість способів вибрати підмножину з масиву з сумою елементів, рівною .

Потім переберемо всі підмножини масиву . Нехай — сума елементів однієї з таких підмножин. Якщо об'єднати цю підмножину з усіма підмножинами масиву , сума елементів яких дорівнює s - sum, то в результаті вийде підмножина вхідного масиву, сума елементів якої дорівнює .

Приклад

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

Переберемо всі підмножини множини a = \{1, 3, 2\}:

\{1\}: sum = 1, m[1]++;

\{1, 2\}: sum = 3, m[3]++;

\{3\}: sum = 3, m[3]++;

\{3, 2\}: sum = 5, m[5]++;

\{2\}: sum = 2, m[2]++;

\{1, 3, 2\}: sum = 6, m[6]++;

\{1, 3\}: sum = 4, m[4]++;

\{\}: sum = 0, m[0]++;

Структура після обробки буде містити наступні значення:

Тепер переберемо всі підмножини множини b = \{2, 1, 4\}. У даному прикладі шуканою сумою є s = 7. Для кожної суми , що відповідає підмножині , будемо додавати до відповіді значення m[s - sum].

Шукана сума дорівнює 1 + 1 + 2 + 1 + 1 + 1 + 1 + 0 = 8, що і є відповіддю.

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

Оголосимо робочі структури даних.

vector<int> a, b;
unordered_map<long long, long long> m;

Читаємо вхідні дані. Першу половину послідовності заносимо в масив , другу — в масив .

scanf("%d %d", &n, &s);
a.resize(n / 2);
for (i = 0; i < n / 2; i++)
  scanf("%d", &a[i]);

b.resize(n - n / 2);
for (i = n / 2; i < n; i++)
  scanf("%d", &b[i - n / 2]);

Перебираємо всі підмножини послідовності і обчислюємо суми їхніх елементів. У структурі unordered_map зберігаємо, скільки разів кожна сума зустрічається серед цих підмножин. Якщо — сума поточної підмножини, то значення збільшуємо на .

for (i = 0; i < (1 << a.size()); i++)
{
  sum = 0;

Змінна містить бітову маску поточної підмножини . Сума її елементів обчислюється у змінній .

      for (j = 0; j < a.size(); j++)
        if (i & (1 << j)) sum += a[j];

Для кожної знайденої суми sum збільшуємо значення на .

  m[sum]++;
}

Кількість способів отримати суму з елементів вихідного масиву підраховуємо у змінній .

res = 0;

Перебираємо всі підмножини послідовності .

for (i = 0; i < (1 << b.size()); i++)
{
  sum = 0;

Змінна містить бітову маску поточної підмножини . Сума елементів цієї підмножини обчислюється у змінній .

  for (j = 0; j < b.size(); j++)
    if (i & (1 << j)) sum += b[j];

Якщо об'єднати поточну підмножину множини , сума елементів якої дорівнює , з усіма підмножинами множини , сума яких дорівнює s - sum, то в результаті вийде підмножина вхідного масиву, сума елементів якої дорівнює .

  if (m.count(s - sum) > 0) res += m[s - sum];
}

Виводимо відповідь.

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