Біноміальний коефіцієнт Сполученням з n елементів по k називається набір з k елементів, вибраних із заданих n елементів. При цьому набори, які відрізняються тільки порядком слідування елементів (але не складом), вважаються однаковими. Саме завдяки цій властивості сполучення відрізняються від розміщень.
Наприклад, розглянемо множину з 5 елементів: \{1, 2, 3, 4, 5\} . Набори \{2, 1, 3\} і \{3, 2, 1\} є однаковими як сполучення (хоча різні як розміщення), оскільки складені з тих самих елементів \{1, 2, 3\} .
Формула для обчислення кількості сполучень з n по k дорівнює
C_n^{k} = {n! \over k! \cdot (n - k)!}
Числа C_n^{k} називаються біноміальними коефіцієнтами .
Коли ми вибираємо нуль елементів із множини з n елементів, ми фактично робимо “пустий” вибір. Порожня множина, в цьому контексті, вважається допустимим сполученням. Уявіть, що у вас є коробка з n кулями, і ви хочете вибрати 0 з них. Навіть якщо ви нічого не вибираєте, це все одно є допустимим варіантом вибору. Таким чином, C_n^{0} означає кількість способів вибрати 0 елементів з n , що дорівнює 1 .
Ми можемо розглядати це як один єдиний спосіб не вибирати жоден елемент з множини, що і дає результат 1 .
C_n^{0} = {n! \over 0! \cdot (n - 0)!} = {n! \over 1! \cdot n!} = 1
Коли ми вибираємо один елемент із множини з n елементів, у нас є n варіантів вибору: ми можемо вибрати будь-який з n елементів. Наприклад, якщо у нас є множина з чисел від 1 до 5 ( \{1, 2, 3, 4, 5\} ), то ми можемо вибрати одне число з цієї множини, і у нас є 5 варіантів для цього вибору.
Значення C_n^{1} дорівнює n , тому що є n способів вибрати один елемент з множини з n елементів.
C_n^{1} = {n! \over 1! \cdot (n - 1)!} = n
При виборі 2 елементів із n — елементної множини, ми формуємо комбінації пар. Для побудови такої пари ми спочатку вибираємо один елемент, а потім другий.
Перший елемент можна вибрати з n елементів.
Після вибору першого елемента, у нас залишається n –1 елемент для вибору другого елемента (оскільки ми не можемо вибрати повторно той самий елемент).
Таким чином, загальна кількість комбінацій пар елементів, які можна вибрати з n — елементної множини, дорівнює добутку числа способів вибрати перший і другий елементи: n \cdot (n – 1) .
Проте, кожна пара врахована двічі, оскільки порядок елементів у парі не має значення. Наприклад, ( p , q ) і ( q , p ) вважаються однією і тією ж парою. Щоб врахувати це, слід поділити загальну кількість комбінацій на 2 . Таким чином, C_n^{2} дорівнює n \cdot (n – 1) / 2 .
C_n^{2} = {n! \over 2! \cdot (n - 2)!} = {n \cdot (n - 1) \over 2}
Обчислимо значення наступних біноміальних коефіцієнтів:
C_4^{2} = {4! \over 2! \cdot 2!} = {3 \cdot 4 \over 2} = 6,
C_6^{3} = {6! \over 3! \cdot 3!} = {4 \cdot 5 \cdot 6 \over 6} = 20,
C_7^{3} = {7! \over 3! \cdot 4!} = {5 \cdot 6 \cdot 7 \over 6} = 35
Властивість симетрії: C_n^{k} = C_n^{n - k} .
Кількість способів вибрати k елементів з n дорівнює кількості способів не вибрати ( n – k ) елементів з n . Але не вибрати ( n – k ) елементів з n ми можемо C_n^{n - k} способами. Відповідно C_n^{k} = C_n^{n - k} .
Обчислимо значення наступних біноміальних коефіцієнтів:
C_5^{4} = C_5^{5 - 4} = C_5^{1} = 5,
C_6^{4} = C_6^{6 - 4} = C_6^{2} = {6 \cdot 5 \over 2} = 15
Вправа. Обчислити значення наступних біноміальних коефіцієнтів:
1) C_5^{2}
3) C_5^{4}
5) C_8^{1}
2) C_8^{3}
4) C_7^{5}
6) C_8^{5}
Формула додавання: C_n^{k} = C_{n-1}^{k-1} + C_{n-1}^{k} .
Доказ. Обчислимо значення правої частини рівняння:
C_{n-1}^{k-1} + C_{n-1}^{k} = {(n - 1)! \over (k - 1)! \cdot (n - k)!} + {(n - 1)! \over k! \cdot (n - k - 1)!} =
{(n - 1)! \cdot k + (n - 1)! \cdot (n - k) \over k! \cdot (n - k)!} = {(n - 1)! \cdot (k + n - k)\over k! \cdot (n - k)!} = {n! \over k! \cdot (n - k)!} = C_n^{k}
Готуючись до іспиту з математичного аналізу, Петя розклав перед собою n різних шпаргалок. Вони були його порятунком, адже за весь семестр Петя так жодного разу не спромігся вивчити матеріал як слід. Шпаргалок виявилося настільки багато, що вони не вміщалися в жодну кишеню. Тому Петя вирішив підрахувати максимальну кількість шпаргалок, яку він зможе взяти з собою на іспит. І тут виникло питання: а скільки взагалі існує способів вибрати потрібну кількість шпаргалок?
Вхідні дані. Містять загальну кількість шпаргалок n~(1 \le n \le 12) і кількість шпаргалок k~(0 \le k \le n) , яку Петя може взяти з собою.
Вихідні дані. Виведіть кількість способів вибрати k шпаргалок з n .
Відкрити задачу
Для обчислення біноміального коефіцієнта можна скористатися наступним співвідношенням:
C_n^{k} = {n! \over k! \cdot (n - k)!} = {n \cdot (n - 1) \cdot (n - 2) \cdot ... \cdot (n - k + 1) \over 1 \cdot 2 \cdot 3 \cdot ... \cdot k}
Оголосимо змінну r es і ініціалізуємо її значенням 1 . Потім помножимо r es на n і розділимо результат на 1 . Після цього помножимо r es на n –1 і розділимо на 2 . Процес множень і ділення будемо продовжувати k разів (чисельник і знаменник C_n^{k} після скорочення на ( n – k )! містить k множників).
Функція Cnk обчислює значення біноміального коефіцієнта ітеративним методом.
int Cnk(int n, int k)
{
int res = 1;
for(int i = 1; i <= k; i++)
res = res * (n - i + 1) / i;
return res;
}Основна частина програми. Читаємо вхідні дані.
Обчислюємо і виводимо відповідь.
res = Cnk(n,k);
printf("%d\n",res);Для рекурсивної реалізації біноміального коефіцієнта можна скористатися співвідношенням:
C_n^{k} = \begin{cases}
C_{n - 1}^{k - 1} + C_{n - 1}^{k}, n > 0 \\
1, k = n~або~k = 0
\end{cases}
Функція Cnk обчислює значення біноміального коефіцієнта рекурсивним методом.
int Cnk(int n, int k)
{
if (n == k) return 1;
if (k == 0) return 1;
return Cnk(n - 1, k - 1) + Cnk(n - 1, k);
}Основна частина програми. Читаємо вхідні дані.
Обчислюємо і виводимо відповідь.
res = Cnk(n,k);
printf("%d\n",res);Скількома способами серед n ЛКШат можна вибрати k таких, яким дістанеться кефір? Відповідь виведіть за модулем 9929 .
Вхідні дані. Цілі числа n і k~(0 \le k \le n \le 500) .
Вихідні дані. Виведіть шукану кількість способів за модулем 9929 .
Відкрити задачу
Відповіддю до задачі буде значення C_n^{k}~mod~9929 . Оскільки слід знайти біноміальний коефіцієнт за модулем, постараємось при обчисленнях уникнути ділення. Для цього скористаємося співвідношенням C_n^{k} = C_{n - 1}^{k} + C_{n - 1}^{k - 1}, C_n^{0} = 1 .
Оскільки k \le n \le 500 , то використовуємо техніку мемоізації.
Оголосимо константи і масив c nk , для якого cnk[n][k] = C_n^{k}~mod~9929 .
#define MAX 510
#define MOD 9929
int cnk[MAX][MAX];Функція c обчислює значення біноміального коефіцієнта C_n^{k}~mod~9929 .
int c(int n, int k)
{
if (cnk[n][k] > 0) return cnk[n][k];
if (n - k < k) return c(n,n-k);
if (!k) return cnk[n][k] = 1;
return cnk[n][k] = (c(n-1,k) + c(n-1,k-1)) % MOD;
}Основна частина програми. Ініціалізуємо масив c nk нуля
ми. Читаємо вхідні дані.
memset(cnk,0,sizeof(cnk));
scanf("%d %d",&n,&k);Обчислюємо і виводимо відповідь.
Біноміальні коефіцієнти з'являються в біномі Ньютона:
(x + y)^n = \sum_{k=0}^n C_n^{k}x^{k}y^{n-k}
Розглянемо приклади:
(x + y)^2 = C_2^{0}x^{2}y^{0} + C_2^{1}x^{1}y^{1} + C_2^{2}x^{0}y^{2} = x^2 + 2xy + y^2,
(x + y)^3 = C_3^{0}x^{3}y^{0} + C_3^{1}x^{2}y^{1} + C_3^{2}x^{1}y^{2} + C_3^{3}x^{0}y^{3} = x^3 + 3x^2y + 3xy^2 + y^3
За заданим невід'ємним цілим числом n знайдіть суму біноміальних коефіцієнтів
C_n^{0} + C_n^{1} + ... + C_n^{n}
Вхідні дані. Одне невід'ємне ціле число n~(n \le 60) .
Вихідні дані. Виведіть значення суми.
Відкрити задачу
Формула бінома Ньютона має вигляд:
(a + b)^n = \sum_{i = 0}^n{C_n^ia^ib^{n-i}}
Якщо покласти a = b = 1 , то дане співвідношення приймає такий вигляд:
(1 + 1)^n = \sum_{i = 0}^n{C_n^i1^i1^{n-i}}
або
2^n = \sum_{i = 0}^n{C_n^i} = C_n^0 + C_n^1 + ... + C_n^n
Таким чином, вказана сума дорівнює 2^n .
Приклад
При n = 1 : C_1^0 + C_1^1 = 1 + 1 = 2
При n = 2 : C_2^0 + C_2^1 + C_2^2 = 1 + 2 + 1 = 4
При n = 3 : C_3^0 + C_3^1 + C_3^2 + C_3^3 = 1 + 3 + 3 + 1 = 8
За заданим невід'ємним цілим числом n знайдіть суму біноміальних коефіцієнтів
(C_n^{0})^2 + (C_n^{1})^2 + (C_n^{2})^2 + ... + (C_n^{n})^2
Вхідні дані. Одне невід'ємне ціле число n~(n \le 30) .
Вихідні дані. Виведіть значення суми.
Відкрити задачу
Розглянемо 2 n об'єктів a_1, ..., a_{2n} і знайдемо кількість способів вибрати n об'єктів з 2 n .
Розіб'ємо множину на дві частини: \{a_1, a_2, ..., a_n, a_{n+1}, ..., a_{2n}\} . Щоб вибрати n об'єктів з 2 n , нам потрібно вибрати k~(k \le n) об'єктів з лівої половини (тобто з множини \{a_1, a_2, ..., a_n \}) і n – k об'єктів з правої (з множини \{a_{n+1}, a_{n+2}, ..., a_{2n}\}) . Кількість способів зробити такий вибір відповідно до правила множення дорівнює C_n^k \cdot C_n^{n-k} = (C_n^k)^2 . Оскільки k може приймати значення від 0 до n , то \sum_{k=0}^n{(C_n^k)^2} дорівнює кількості способів вибрати n об'єктів з 2 n , тобто C_{2n}^n . Таким чином
(C_n^0)^2 + (C_n^1)^2 + (C_n^2)^2 + ... + (C_n^n)^2 = C_{2n}^n
Приклад
Для n = 3 відповідь дорівнює (C_3^0)^2 + (C_3^1)^2 + (C_3^2)^2 + (C_3^3)^2 = 1^2 + 3^2 + 3^2 + 1^2 = 20
Водночас C_6^3 = {6! \over 3! \cdot 3!} = {4 \cdot 5 \cdot 6 \over 6} = 20 .
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
long long Cnk(long long n, long long k)
{
long long res = 1;
if (k > n - k) k = n - k;
for (long long i = 1; i <= k; i++)
res = res * (n - i + 1) / i;
return res;
}Основна частина програми. Читаємо вхідне значення n .
Обчислюємо і виводимо відповідь.
res = Cnk(2*n, n);
printf("%lld\n", res);За заданими значеннями k і n знайдіть кількість натуральних розв'язків рівняння
x_1 + x_2 + ... + x_k = n
Вхідні дані. Два натуральні числа k і n~(k \le n \le 100) .
Вихідні дані. Виведіть кількість натуральних розв'язків для заданого рівняння. Відомо, що відповідь не перевищує 10^{18} .
Відкрити задачу
Розглянемо послідовність із n одиниць: 111...11 . Між будь-якими двома одиницями можна вставити знак ‘ + ’. Наприклад 11+111+1 . Такий запис буде позначати суму 2 + 3 + 1 , де кожен доданок – кількість одиниць, що стоять поряд. Кількість позицій, куди можна вставити знак ‘ + ’, дорівнює n –1 . Оскільки необхідно отримати суму з k доданків, то слід вставити k –1 знак ‘ + ’.
Нам слід вставити k –1 плюс на n –1 місце. Це можна зробити C_{n-1}^{k-1} способами.
Приклад
Розглянемо рівняння x_1 + x_2 + x_3 = 4 . Воно має 3 позитивних цілих розв'язки:
Наприклад, n = 4 одиниці на k = 3 доданків можна розбити C_2^3 = 3 способами:
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
long long Cnk(long long k, long long n)
{
long long res = 1;
if (k > n - k) k = n - k;
for (long long i = 1; i <= k; i++)
res = res * (n - i + 1) / i;
return res;
}Основна частина програми. Читаємо вхідні дані.
scanf("%lld %lld", &k, &n);Обчислюємо і виводимо відповідь C_{n-1}^{k-1} .
res = Cnk(k - 1, n - 1);
printf("%lld\n", res);За заданими значеннями k і n знайдіть кількість невід'ємних цілих розв'язків рівняння
x_1 + x_2 + ... + x_k = n
Вхідні дані. Два натуральні числа k і n~(k \le n \le 100) .
Вихідні дані. Виведіть кількість невід'ємних цілих розв'язків для заданого рівняння. Відомо, що відповідь не перевищує 10^{18} .
Відкрити задачу
Розглянемо послідовність із n + k – 1 позицій. У n позиціях слід поставити 1 . У k –1 позицію слід поставити символ ' + ' (щоб отримати k доданків).
Будь-якому розташуванню одиниць і знаків ' + ' по позиціях буде відповідати деякий розв'язок заданого рівняння. Наприклад, розглянемо деякі розв'язки рівняння x_1 + x_2 + x_3 = 4 (4 одиниці і 2 плюси):
Нам слід вставити k –1 плюс на n + k – 1 позицій. Це можна зробити C_{n+k-1}^{k-1} способами.
Приклад
Розглянемо рівняння x_1 + x_2 + x_3 = 4 . Його розв'язками будуть:
(4, 0, 0) і її 3 перестановки
(3, 1, 0) і її 6 перестановок
(2, 2, 0) і її 3 перестановки
(2, 1, 1) і її 3 перестановки
Усього є 3 + 6 + 3 + 3 = 15 розв'язків.
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
long long Cnk(long long k, long long n)
{
long long res = 1;
if (k > n - k) k = n - k;
for (long long i = 1; i <= k; i++)
res = res * (n - i + 1) / i;
return res;
}Основна частина програми. Читаємо вхідні дані.
scanf("%lld %lld", &k, &n);Обчислюємо і виводимо відповідь C_{n+k-1}^{k-1} .
res = Cnk(k - 1, n + k - 1);
printf("%lld\n", res);Нехай n — ціле невід'ємне число. Позначимо
n! = 1 \cdot 2 \cdot ... \cdot n~(0! = 1),
C_n^{k} = {n! \over k! \cdot (n - k)!} (0 \le k \le n)
Для заданих n і k обчисліть значення C_n^{
k} .
Вхідні дані. Перша строка містить кількість тестів t~(t \le 50) . Кожна з наступних t строк містить два цілих числа n і k~(0 \le n < 2^{64}, 0 \le C_n^{k} < 2^{64}) .
Вихідні дані. Виведіть t строк, кожна з яких містить значення C_n^{k} для відповідного тесту.
Відкрити задачу
Обчислення будемо проводити з використанням 64 -розрядних беззнакових цілих чисел (unsigned long long). Очевидно, що
C_n^k = {n! \over k! \cdot (n - k)!} = {n \cdot (n - 1) \cdot (n - 2) \cdot ... \cdot (n - k + 1) \over 1 \cdot 2 \cdot 3 \cdot ... \cdot k} = {n \over 1} \cdot {n - 1 \over 2} \cdot {n - 2 \over 3} \cdot ... \cdot {n - k + 1 \over k}
Присвоїмо змінній r es значення 1 , після чого будемо її множити на {n - i + 1 \over i} для всіх i від 1 до k . Кожного разу ділення на i буде цілочисельним, проте при множенні можна отримати переповнення. Нехай d = НОД(res, i) . Тоді операцію
res = res * (n – i + 1 ) / i
перепишемо через
res = (res / d) * ((n – i + 1 ) / (i / d))
При такій реалізації переповнення при множенні не відбудеться (відповідь є 64 -розрядним беззнаковим цілим числом). Зазначимо, що спочатку слід виконати ділення (n – i + 1 ) / (i / d) , а потім множення r es / d на отримане частке.
Для обчислення C_n^k слід виконати k ітерацій. Але що робити, якщо слід обчислити C_{2000000000}^{1999999999} ? Відповідь не перевищить 2^{64} , тому такі вхідні дані можливі. Оскільки C_n^k = C_n^{n - k} , то при n – k < k слід покласти k = n – k .
Приклад
Розглянемо наступний приклад:
C_6^3 = {6 \over 1} \cdot {5 \over 2} \cdot {4 \over 3} = 15 \cdot {4 \over 3}
Нехай res = 15 , і залишилось виконати множення res \cdot {4 \over 3} = 15 * {4 \over 3} . Обчислимо d = НОД(15, 3) = 3 . Тому 15 \cdot {4 \over 3} = (15 / 3) \cdot {4 \over 3 / 3} = 5 \cdot {4 \over 1} = 20 .
Функція gcd обчислює найбільший спільний дільник чисел a і b .
unsigned long long gcd(unsigned long long a, unsigned long long b)
{
return (!b) ? a : gcd(b,a % b);
}Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
unsigned long long Cnk(unsigned long long n, unsigned long long k)
{
unsigned long long CnkRes = 1, t, i;
if (k > n - k) k = n - k;
for(i = 1; i <= k; i++)
{
t = gcd(CnkRes, i);
CnkRes = (CnkRes / t) * ((n - i + 1) / (i / t));
}
return CnkRes;
}Основна частина програми. Читаємо вхідні дані. Послідовно обробляємо тести.
scanf("%d",&t);
while(t--)
{
scanf("%llu %llu",&n,&k);
res = Cnk(n,k);
printf("%llu\n",res);
}Дано натуральне число n . Знайдіть значення наступної суми
1 \cdot C_n^{0} + 2 \cdot C_n^{1} + 3 \cdot C_n^{2} + ... + (n + 1) \cdot C_n^{n}
Вхідні дані. Одне натуральне число n\:(n \le 30) .
Вихідні дані. Виведіть значення шуканої суми.
Відкрити задачу
Перепишемо задану суму у вигляді:
1 \cdot C_n^0 + 2 \cdot C_n^1 + 3 \cdot C_n^2 + ... + (n + 1) \cdot C_n^n =
\left( C_n^0 + C_n^1 + C_n^2 + ... + C_n^n \right) + 1 \cdot C_n^1 + 2 \cdot C_n^2 + ... + n \cdot C_n^n
Сума у дужках дорівнює 2^n . Якщо у формулі бінома Ньютона
(a + b)^n = \sum_{i=0}^n{C_n^ia^ib^{n-i}}
покласти a = b = 1 , то отримаємо співвідношення: (1 + 1)^n = \sum_{i=0}^n{C_n^i1^i1^{n-i}} , або
2^n = \sum_{i=0}^nC_n^i = C_n^0 + C_n^1 + ... + C_n^n
Обчислимо залишок суми:
1 \cdot C_n^1 + 2 \cdot C_n^2 + ... + n \cdot C_n^n =
1 \cdot {n! \over 1! \cdot (n - 1)!} + 2 \cdot {n! \over 2! \cdot (n - 2)!} + ... + n \cdot {n! \over n! \cdot (n - n)!} =
n \cd
от \left( {(n - 1)! \over 0! \cdot (n - 1)!} + {(n - 1)! \over 1! \cdot (n - 2)!} + ... + {(n - 1)! \over (n - 1)! \cdot (n - n)!} \right) =
n \cdot \left( C_{n-1}^0 + C_{n-1}^1 + ... + C_{n-1}^{n-1} \right) = n \cdot 2^{n - 1}
Таким чином, відповідь дорівнює 2^n + n \cdot 2^{n - 1} = (n + 2) \cdot 2^{n - 1} .
Приклад
Обчислимо відповідь для n = 3 . При безпосередньому обчисленні:
1 \cdot C_3^0 + 2 \cdot C_3^1 + 3 \cdot C_3^2 + 4 \cdot C_3^3 = 1 \cdot 1 + 2 \cdot 3 + 3 \cdot 3 + 4 \cdot 1 = 1 + 6 + 9 + 4 = 20
При обчисленні за формулою: 5 \cdot 2^2 = 20 .
Читаємо вхідне значення n .
Обчислюємо і виводимо відповідь.
res = (1LL << (n - 1)) * (n + 2);
printf("%lld\n", res);Реалізація алгоритму – функція
Оголосимо масив для запам'ятовування результатів: dp[n][k] = C_n^k .
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
int Cnk(int n, int k)
{
if (n == k) return 1;
if (k == 0) return 1;
if (dp[n][k] != -1) return dp[n][k];
return dp[n][k] = (Cnk(n - 1, k - 1) + Cnk(n - 1, k)) % 9929;
}Основна частина програми. Читаємо вхідне значення n .
scanf("%d", &n);
memset(dp, -1, sizeof(dp));Обчислюємо зазначену суму.
res = 0;
for (i = 0; i <= n; i++)
res += (i + 1) * Cnk(n, i);Виводимо відповідь.
У вас є аркуш паперу, і ви обираєте на ньому прямокутник розміром n \cdot m . Назвемо цей прямокутник разом з лініями, на яких він знаходиться, сіткою. Починаючи з лівого нижнього кута сітки, ви переміщаєте олівець до правого верхнього кута, стежачи за тим, щоб він залишався на лініях і рухався тільки вправо або вгору. Результат показаний зліва:
Дійсно шедевр, чи не так? Повторюючи процедуру ще раз, ви отримаєте зображення, показане праворуч. Тепер виникає питання: скільки різних витворів мистецтва можна таким чином створити?
Вхідні дані. Два натуральні числа n і m .
Вихідні дані. Виведіть кількість різних витворів мистецтва, які можна створити, використовуючи описану вище процедуру. Можна стверджувати, що це число відповідає 64 — бітовому знаковому цілому числу.
Відкрити задачу
Шуканий шлях являє собою ламану, що складається з n + m ланок. З них n ланок повинні бути вертикальними, а решта — горизонтальними. Кількість варіантів вибрати n вертикальних ланок з n + m дорівнює C_{n+m}^n .
Приклад
Для першого прикладу n = 3, m = 4 . Відповідь дорівнює C_7^3 = {7! \over 3! \cdot 4!} = {7 \cdot 6 \cdot 5 \over 1 \cdot 2 \cdot 3} = 35 .
Для другого прикладу n = 1, m = 1 . Відповідь дорівнює C_2^1 = {2! \over 1! \cdot 1!} = 2 .
Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
long long Cnk(long long n, long long k)
{
long long res = 1;
if (k > n - k) k = n - k;
for (long long i = 1; i <= k; i++)
res = res * (n - i + 1) / i;
return res;
}Основна частина програми. Читаємо вхідні дані.
scanf("%lld %lld", &n, &m);Обчислюємо і виводимо відповідь C_{n+m}^n .
res = Cnk(n + m, n);
printf("%lld\n", res);Гуннар — досить старий і забудькуватий дослідник. Зараз він пише статтю про безпеку в соціальних мережах, яка включає в себе деякі елементи комбінаторики. Він написав програму для обчислення біноміальних коефіцієнтів, щоб перевірити деякі розрахунки.
Біноміальний коефіцієнт C_n^{k} — це число
C_n^{k
} = {n! \over k! \cdot (n - k)!}
де n і k — невід'ємні числа.
Гуннар використовує свою програму для обчислення C_n^{k} і отримав число m як результат. На жаль, оскільки він забудькуватий, він забув числа n і k , які він використовував як вхідні дані. Ці два числа були результатом довгих обчислень, і вони були записані на одному з численних аркушів, що лежать на його столі. Замість того щоб шукати в паперах, він вирішив відновити числа n і k за отриманою відповіддю. Чи можете ви допомогти йому знайти всі такі можливі значення?
Вхідні дані. Перша строка містить кількість тестів, не більше 100 . Кожен тест задається в одній строкі і містить ціле число m~(2 \le m \le 10^{15}) — результат програми Гуннара.
Вихідні дані. Для кожного тесту виведіть дві строки. Перша строка повинна містити кількість способів виразити m за допомогою біноміального коефіцієнта. У другій строкі повинні розташовуватись всі пари ( n , k ) , що задовольняють C_n^{k} = m . Пари слід розташувати за зростанням n , а у випадку рівності за зростанням k . Формат виводу пар наведено в прикладі.
1
(2,1)
4
(6,2) (6,4) (15,1) (15,14)Відкрити задачу
Якщо C_n^k = m , то C_n^{n - k} = m . Достатньо знайти розв'язок k \le n / 2 і разом з парою ( k , n ) вивести також пару (k, n – k) . При k = n / 2 ці дві пари співпадають.
Нехай p — найменше число, для якого C_{2p}^p > m . Тоді очевидно що 0 \le k < p .
Зафіксуємо таке k що C_{2k}^k \le m і розглянемо функцію f(n) = C_n^k . Тоді при 2k \le n \le m функція f ( n ) є монотонно зростаючою. Відповідно можна розв'язати рівняння f(n) = m бінарним пошуком.
Таким чином для розв'язання задачі слід перебрати значення k~(0 \le k < p) і для кожного такого k розв'язати рівняння C_n^k = m щодо n бінарним пошуком. Знайдене значення n повинно бути цілочисельним.
Приклад
Розглянемо рівняння C_n^k = 3003 . Зважаючи на те що C_{12}^6 = 924 , а C_{14}^7 = 3432 , то нам достатньо перебрати 0 \le k \le 6 .
Нехай k = 2 , розглянемо рівняння C_n^2 = 3003 або {n \cdot (n - 1) \over 2} = 3003 , n \cdot (n – 1) = 6006 . Бінарним пошуком в проміжку 4 \le n \le 3003 знаходимо цілочисельне розв'язання n = 78 . Зважаючи на те що n \ne 2 \cdot k , маємо два розв'язки: C_{78}^2 = C_{78}^{76} = 3003 .
Шукані пари будемо зберігати у векторі пар r es .
vector<pair<long long,long long> > res;Функція Cnk обчислює значення біноміального коефіцієнта C_n^k .
long long Cnk(long long n, long long k)
{
long long i, Res = 1;
if (k > n - k) k = n - k;
for(i = 1; i <= k; i++)
{Якщо на черговій ітерації результат перевищує m (ми шукаємо розв'язок рівняння C_n^k = m ), то зупиняємось. Таким виходом з функції ми також уникнемо переповнення.
if (1.0 * Res * (n - i + 1) / i > m) return m + 1;
Res = Res * (n - i + 1) / i;
}
return Res;
}Основна частина програми. Читаємо вхідні дані.
scanf("%d",&tests);
while (tests--)
{
res.clear();
scanf("%lld",&m);Перебираємо значення k від 0 до тих пір поки C_{2k}^k ≤ m .
for(k = 0; Cnk(2*k,k) <= m; k++)
{Шукаємо значення n~(2k \le n \le m) бінарним пошуком.
long long lo = 2*k, hi = m;
while (lo < hi)
{
long long n = (lo + hi) / 2;
if (Cnk(n,k) < m) lo = n + 1; else hi = n;
}Якщо C_{lo}^k = m , то розв'язок знайдено. Заносимо в результат одну або дві пари розв'язків.
if (Cnk(lo,k) == m)
{
res.push_back(make_pair(lo,k));
if (lo != 2*k)
res.push_back(make_pair(lo,lo - k));
}
}Сортуємо пари.
sort(res.begin(),res.end());Виводимо відповідь — кількість знайдених пар і самі пари.
printf("%d\n",res.size());
for(i = 0; i < res.size(); i++)
printf("(%lld,%lld) ", res[i].first,res[i].second);
printf("\n");
}Асимптотичні оцінки для біноміального коефіцієнта Теорема 1. Між біноміальними коефіцієнтами має місце співвідношення:
C_n^0 < C_n^1 < ... < C_n^{\lfloor n/2 \rfloor} \ge C_n^{\lfloor n/2 \rfloor + 1} > ... > C_n^n
Теорема 2. C_{2n}^n < 4^n . Слід із того, що \sum_{k = 0}^{2n} C_{2n}^k = (1 + 1)^{2n} = 2^{2n} =
4^n
Теорема 3. C_{2n}^n > {4^n \over 2n + 1} . Слід із того, що \sum_{k = 0}^{2n} C_{2n}^k = 4^n , а самих коефіцієнтів 2n + 1 . Тому більший з коефіцієнтів буде більшим {4^n \over 2n + 1} .
Теорема 4. Формула Стірлінга. n! ≈ \sqrt{2 \pi n}\left({n \over e} \right) ^ n .
Більш точне наближення має вигляд:
n! ≈ \sqrt{2 \pi n} \left({n \over e} \right) ^ n \left( 1 + {1 \over 12n} + {1 \over 288n^2} - {139 \over 51849n^3} - {571 \over 2488320n^4} \right)
Теорема 5.
C_{2n}^n ≈ {\sqrt{4 \pi n}\left({2n \over e} \right) ^ {2n} \over \left( \sqrt{2 \pi n}\left({n \over e} \right) ^ n \right) ^2} = {4^n \over \sqrt{пі n}}
Теорема 6.
C_n^k = {n! \over k!(n - k)!} = { n \cdot (n - 1) \cdot (n - 2) \cdot ... \cdot (n - k + 1) \over 1 \cdot 2 \cdot 3 \cdot ... \cdot k} =
{n! \over k!} \cdot \left( 1 - {1 \over n} \right) \cdot \left( 1 - {2 \over n} \right) \cdot ... \cdot \left( 1 - {k - 1 \over n} \right) =
{n! \over k!} \cdot e ^{ln \left( 1 - {1 \over n} \right) + ln \left( 1 - {2 \over n} \right) + ... + ln \left( 1 - {k - 1 \over n} \right)} \le (скористаємося~нерівністю~ln(1 - x) \le -x)
{n! \over k!} \cdot e ^{-{1 \over n} - {2 \over n} - ... - {k - 1 \over n}} = {n! \over k!} \cdot e^{-{k \cdot (k - 1) \over 2n}}
Список використаних задач
Коментарі