Basecamp
    Головна
    Задачі
    Змагання
    Курси
    Рейтинг
    Дописи
    Discord
Біноміальний коефіцієнт
Увійти
medv
•Статті•1 рік тому

Біноміальний коефіцієнт

Сполученням з n елементів по k називається набір з k елементів, вибраних із заданих n елементів. При цьому набори, які відрізняються тільки порядком слідування елементів (але не складом), вважаються однаковими. Саме завдяки цій властивості сполучення відрізняються від розміщень.

Наприклад, розглянемо множину з 5 елементів: {1,2,3,4,5}. Набори {2,1,3} і {3,2,1} є однаковими як сполучення (хоча різні як розміщення), оскільки складені з тих самих елементів {1,2,3}.

Формула для обчислення кількості сполучень з n по k дорівнює

Cnk​=k!⋅(n−k)!n!​

Числа Cnk​ називаються біноміальними коефіцієнтами.

Приклад

Cn0​=1

Коли ми вибираємо нуль елементів із множини з n елементів, ми фактично робимо “пустий” вибір. Порожня множина, в цьому контексті, вважається допустимим сполученням. Уявіть, що у вас є коробка з n кулями, і ви хочете вибрати 0 з них. Навіть якщо ви нічого не вибираєте, це все одно є допустимим варіантом вибору. Таким чином, Cn0​ означає кількість способів вибрати 0 елементів з n, що дорівнює 1.

Ми можемо розглядати це як один єдиний спосіб не вибирати жоден елемент з множини, що і дає результат 1.

Cn0​=0!⋅(n−0)!n!​=1!⋅n!n!​=1
Приклад

Cn1​=n

Коли ми вибираємо один елемент із множини з n елементів, у нас є n варіантів вибору: ми можемо вибрати будь-який з n елементів. Наприклад, якщо у нас є множина з чисел від 1 до 5 ({1,2,3,4,5}), то ми можемо вибрати одне число з цієї множини, і у нас є 5 варіантів для цього вибору.

Значення Cn1​ дорівнює n, тому що є n способів вибрати один елемент з множини з n елементів.

Cn1​=1!⋅(n−1)!n!​=n
Приклад

Cn2​=2n⋅(n−1)​

При виборі 2 елементів із n — елементної множини, ми формуємо комбінації пар. Для побудови такої пари ми спочатку вибираємо один елемент, а потім другий.

  • Перший елемент можна вибрати з n елементів.

  • Після вибору першого елемента, у нас залишається n–1 елемент для вибору другого елемента (оскільки ми не можемо вибрати повторно той самий елемент).

Таким чином, загальна кількість комбінацій пар елементів, які можна вибрати з n — елементної множини, дорівнює добутку числа способів вибрати перший і другий елементи: n⋅(n–1).

Проте, кожна пара врахована двічі, оскільки порядок елементів у парі не має значення. Наприклад, (p,q) і (q,p) вважаються однією і тією ж парою. Щоб врахувати це, слід поділити загальну кількість комбінацій на 2. Таким чином, Cn2​ дорівнює n⋅(n–1)/2.

Cn2​=2!⋅(n−2)!n!​=2n⋅(n−1)​
Приклад

Обчислимо значення наступних біноміальних коефіцієнтів:

C42​=2!⋅2!4!​=23⋅4​=6,
C63​=3!⋅3!6!​=64⋅5⋅6​=20,
C73​=3!⋅4!7!​=65⋅6⋅7​=35

Властивість симетрії: Cnk​=Cnn−k​.

Кількість способів вибрати k елементів з n дорівнює кількості способів не вибрати (n–k) елементів з n. Але не вибрати (n–k) елементів з n ми можемо Cnn−k​ способами. Відповідно Cnk​=Cnn−k​.

Приклад

Обчислимо значення наступних біноміальних коефіцієнтів:

C54​=C55−4​=C51​=5,
C64​=C66−4​=C62​=26⋅5​=15

Вправа. Обчислити значення наступних біноміальних коефіцієнтів:

1) C52​

3) C54​

5) C81​

2) C83​

4) C75​

6) C85​

Формула додавання: Cnk​=Cn−1k−1​+Cn−1k​.

Доказ. Обчислимо значення правої частини рівняння:

Cn−1k−1​+Cn−1k​=(k−1)!⋅(n−k)!(n−1)!​+k!⋅(n−k−1)!(n−1)!​=
k!⋅(n−k)!(n−1)!⋅k+(n−1)!⋅(n−k)​=k!⋅(n−k)!(n−1)!⋅(k+n−k)​=k!⋅(n−k)!n!​=Cnk​
Eolymp #3260. Скільки?

Готуючись до іспиту з математичного аналізу, Петя розклав перед собою n різних шпаргалок. Вони були його порятунком, адже за весь семестр Петя так жодного разу не спромігся вивчити матеріал як слід. Шпаргалок виявилося настільки багато, що вони не вміщалися в жодну кишеню. Тому Петя вирішив підрахувати максимальну кількість шпаргалок, яку він зможе взяти з собою на іспит. І тут виникло питання: а скільки взагалі існує способів вибрати потрібну кількість шпаргалок?

Вхідні дані. Містять загальну кількість шпаргалок n (1≤n≤12) і кількість шпаргалок k (0≤k≤n), яку Петя може взяти з собою.

Вихідні дані. Виведіть кількість способів вибрати k шпаргалок з n.

Вхідні дані 1
3 2
Вихідні дані 1
3
Вхідні дані 2
4 1
Вихідні дані 2
4
Відкрити задачу
Розв'язок - ітеративний метод

Для обчислення біноміального коефіцієнта можна скористатися наступним співвідношенням:

Cnk​=k!⋅(n−k)!n!​=1⋅2⋅3⋅...⋅kn⋅(n−1)⋅(n−2)⋅...⋅(n−k+1)​

Оголосимо змінну res і ініціалізуємо її значенням 1. Потім помножимо res на n і розділимо результат на 1. Після цього помножимо res на n–1 і розділимо на 2. Процес множень і ділення будемо продовжувати k разів (чисельник і знаменник Cnk​ після скорочення на (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;
}
C++
7 lines
118 bytes

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d",&n,&k);

Обчислюємо і виводимо відповідь.

res = Cnk(n,k);
printf("%d\n",res);
Розв'язок - рекурсивний метод

Для рекурсивної реалізації біноміального коефіцієнта можна скористатися співвідношенням:

Cnk​={Cn−1k−1​+Cn−1k​,n>01,k=n або k=0​
Реалізація алгоритму - рекурсивний метод

Функція 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);
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d",&n,&k);

Обчислюємо і виводимо відповідь.

res = Cnk(n,k);
printf("%d\n",res);
Eolymp #5329. Вечірка

Скількома способами серед n ЛКШат можна вибрати k таких, яким дістанеться кефір? Відповідь виведіть за модулем 9929.

Вхідні дані. Цілі числа n і k (0≤k≤n≤500).

Вихідні дані. Виведіть шукану кількість способів за модулем 9929.

Вхідні дані 1
6 3
Вихідні дані 1
20
Відкрити задачу
Розв'язок

Відповіддю до задачі буде значення Cnk​ mod 9929. Оскільки слід знайти біноміальний коефіцієнт за модулем, постараємось при обчисленнях уникнути ділення. Для цього скористаємося співвідношенням Cnk​=Cn−1k​+Cn−1k−1​,Cn0​=1.

Оскільки k≤n≤500, то використовуємо техніку мемоізації.

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

Оголосимо константи і масив cnk, для якого cnk[n][k]=Cnk​ mod 9929.

#define MAX 510
#define MOD 9929
int cnk[MAX][MAX];

Функція c обчислює значення біноміального коефіцієнта Cnk​ 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++
7 lines
181 bytes

Основна частина програми. Ініціалізуємо масив cnk нуля

ми. Читаємо вхідні дані.

memset(cnk,0,sizeof(cnk));
scanf("%d %d",&n,&k);

Обчислюємо і виводимо відповідь.

printf("%d\n",c(n,k));

Біноміальні коефіцієнти з'являються в біномі Ньютона:

(x+y)n=k=0∑n​Cnk​xkyn−k

Розглянемо приклади:

(x+y)2=C20​x2y0+C21​x1y1+C22​x0y2=x2+2xy+y2,
(x+y)3=C30​x3y0+C31​x2y1+C32​x1y2+C33​x0y3=x3+3x2y+3xy2+y3
Eolymp #9892. C0n + ... + Cnn

За заданим невід'ємним цілим числом n знайдіть суму біноміальних коефіцієнтів

Cn0​+Cn1​+...+Cnn​

Вхідні дані. Одне невід'ємне ціле число n (n≤60).

Вихідні дані. Виведіть значення суми.

Вхідні дані
2
Вихідні дані
4
Відкрити задачу
Розв'язок

Формула бінома Ньютона має вигляд:

(a+b)n=i=0∑n​Cni​aibn−i

Якщо покласти a=b=1, то дане співвідношення приймає такий вигляд:

(1+1)n=i=0∑n​Cni​1i1n−i

або

2n=i=0∑n​Cni​=Cn0​+Cn1​+...+Cnn​

Таким чином, вказана сума дорівнює 2n.

Приклад

При n=1: C10​+C11​=1+1=2

При n=2: C20​+C21​+C22​=1+2+1=4

При n=3: C30​+C31​+C32​+C33​=1+3+3+1=8

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

Читаємо вхідне значення n.

scanf("%lld", &n);

Обчислюємо і виводимо відповідь — значення 2n.

res = 1LL << n;
printf("%lld\n", res);
Eolymp #11271. Сума квадратів Cnk

За заданим невід'ємним цілим числом n знайдіть суму біноміальних коефіцієнтів

(Cn0​)2+(Cn1​)2+(Cn2​)2+...+(Cnn​)2

Вхідні дані. Одне невід'ємне ціле число n (n≤30).

Вихідні дані. Виведіть значення суми.

Вхідні дані
3
Вихідні дані
20
Відкрити задачу
Розв'язок

Розглянемо 2n об'єктів a1​,...,a2n​ і знайдемо кількість способів вибрати n об'єктів з 2n.

Розіб'ємо множину на дві частини: {a1​,a2​,...,an​,an+1​,...,a2n​}. Щоб вибрати n об'єктів з 2n, нам потрібно вибрати k (k≤n) об'єктів з лівої половини (тобто з множини {a1​,a2​,...,an​}) і n–k об'єктів з правої (з множини {an+1​,an+2​,...,a2n​}). Кількість способів зробити такий вибір відповідно до правила множення дорівнює Cnk​⋅Cnn−k​=(Cnk​)2. Оскільки k може приймати значення від 0 до n, то ∑k=0n​(Cnk​)2 дорівнює кількості способів вибрати n об'єктів з 2n, тобто C2nn​. Таким чином

(Cn0​)2+(Cn1​)2+(Cn2​)2+...+(Cnn​)2=C2nn​

Приклад

Для n=3 відповідь дорівнює (C30​)2+(C31​)2+(C32​)2+(C33​)2=12+32+32+12=20

Водночас C63​=3!⋅3!6!​=64⋅5⋅6​=20.

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

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
8 lines
177 bytes

Основна частина програми. Читаємо вхідне значення n.

scanf("%lld", &n);

Обчислюємо і виводимо відповідь.

res = Cnk(2*n, n);
printf("%lld\n", res);
Eolymp #11550. x1 + x2 + ... + xk = n

За заданими значеннями k і n знайдіть кількість натуральних розв'язків рівняння

x1​+x2​+...+xk​=n

Вхідні дані. Два натуральні числа k і n (k≤n≤100).

Вихідні дані. Виведіть кількість натуральних розв'язків для заданого рівняння. Відомо, що відповідь не перевищує 1018.

Вхідні дані
3 4
Вихідні дані
3
Відкрити задачу
Розв'язок

Розглянемо послідовність із n одиниць: 111...11. Між будь-якими двома одиницями можна вставити знак ‘+’. Наприклад 11+111+1. Такий запис буде позначати суму 2+3+1, де кожен доданок – кількість одиниць, що стоять поряд. Кількість позицій, куди можна вставити знак ‘+’, дорівнює n–1. Оскільки необхідно отримати суму з k доданків, то слід вставити k–1 знак ‘+’.

Нам слід вставити k–1 плюс на n–1 місце. Це можна зробити Cn−1k−1​ способами.

Приклад

Розглянемо рівняння x1​+x2​+x3​=4. Воно має 3 позитивних цілих розв'язки:

  • (1,1,2)

  • (1,2,1)

  • (2,1,1)

Наприклад, n=4 одиниці на k=3 доданків можна розбити C23​=3 способами:

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

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
8 lines
177 bytes

Основна частина програми. Читаємо вхідні дані.

scanf("%lld %lld", &k, &n);

Обчислюємо і виводимо відповідь Cn−1k−1​.

res = Cnk(k - 1, n - 1);
printf("%lld\n", res);
Eolymp #11551. x1+...+xk = n (2)

За заданими значеннями k і n знайдіть кількість невід'ємних цілих розв'язків рівняння

x1​+x2​+...+xk​=n

Вхідні дані. Два натуральні числа k і n (k≤n≤100).

Вихідні дані. Виведіть кількість невід'ємних цілих розв'язків для заданого рівняння. Відомо, що відповідь не перевищує 1018.

Вхідні дані
3 4
Вихідні дані
15
Відкрити задачу
Розв'язок

Розглянемо послідовність із n+k–1 позицій. У n позиціях слід поставити 1. У k–1 позицію слід поставити символ '+' (щоб отримати k доданків).

Будь-якому розташуванню одиниць і знаків '+' по позиціях буде відповідати деякий розв'язок заданого рівняння. Наприклад, розглянемо деякі розв'язки рівняння x1​+x2​+x3​=4(4 одиниці і 2 плюси):

Нам слід вставити k–1 плюс на n+k–1 позицій. Це можна зробити Cn+k−1k−1​ способами.

Приклад

Розглянемо рівняння x1​+x2​+x3​=4. Його розв'язками будуть:

  • (4,0,0) і її 3 перестановки

  • (3,1,0) і її 6 перестановок

  • (2,2,0) і її 3 перестановки

  • (2,1,1) і її 3 перестановки

Усього є 3+6+3+3=15 розв'язків.

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

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
8 lines
177 bytes

Основна частина програми. Читаємо вхідні дані.

scanf("%lld %lld", &k, &n);

Обчислюємо і виводимо відповідь Cn+k−1k−1​.

res = Cnk(k - 1, n + k - 1); 
printf("%lld\n", res);
Eolymp #318. Біноміальні коефіцієнти 1

Нехай n — ціле невід'ємне число. Позначимо

n!=1⋅2⋅...⋅n (0!=1),
Cnk​=k!⋅(n−k)!n!​(0≤k≤n)

Для заданих n і k обчисліть значення Cnk​.

Вхідні дані. Перша строка містить кількість тестів t (t≤50). Кожна з наступних t строк містить два цілих числа n і k (0≤n<264,0≤Cnk​<264).

Вихідні дані. Виведіть t строк, кожна з яких містить значення Cnk​ для відповідного тесту.

Вхідні дані
6
0 0
1 0
1 1
2 0
2 1
2 2
7 lines
26 bytes
Вихідні дані
1
1
1
1
2
1
Відкрити задачу
Розв'язок

Обчислення будемо проводити з використанням 64-розрядних беззнакових цілих чисел (unsigned long long). Очевидно, що

Cnk​=k!⋅(n−k)!n!​=1⋅2⋅3⋅...⋅kn⋅(n−1)⋅(n−2)⋅...⋅(n−k+1)​=1n​⋅2n−1​⋅3n−2​⋅...⋅kn−k+1​

Присвоїмо змінній res значення 1, після чого будемо її множити на in−i+1​ для всіх 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), а потім множення res/d на отримане частке.

Для обчислення Cnk​ слід виконати k ітерацій. Але що робити, якщо слід обчислити C20000000001999999999​? Відповідь не перевищить 264, тому такі вхідні дані можливі. Оскільки Cnk​=Cnn−k​, то при n–k<k слід покласти k=n–k.

Приклад

Розглянемо наступний приклад:

C63​=16​⋅25​⋅34​=15⋅34​

Нехай res=15, і залишилось виконати множення res⋅34​=15∗34​. Обчислимо d=НОД(15,3)=3. Тому 15⋅34​=(15/3)⋅3/34​=5⋅14​=20.

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

Функція gcd обчислює найбільший спільний дільник чисел a і b.

unsigned long long gcd(unsigned long long a, unsigned long long b)
{
  return (!b) ? a : gcd(b,a % b);
}

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
11 lines
266 bytes

Основна частина програми. Читаємо вхідні дані. Послідовно обробляємо тести.

scanf("%d",&t);
while(t--)
{
  scanf("%llu %llu",&n,&k);
  res = Cnk(n,k);
  printf("%llu\n",res);
}
C++
7 lines
101 bytes
Eolymp #11488. Біноміальна сума

Дано натуральне число n. Знайдіть значення наступної суми

1⋅Cn0​+2⋅Cn1​+3⋅Cn2​+...+(n+1)⋅Cnn​

Вхідні дані. Одне натуральне число n(n≤30).

Вихідні дані. Виведіть значення шуканої суми.

Вхідні дані
3
Вихідні дані
20
Відкрити задачу
Розв'язок

Перепишемо задану суму у вигляді:

1⋅Cn0​+2⋅Cn1​+3⋅Cn2​+...+(n+1)⋅Cnn​=
(Cn0​+Cn1​+Cn2​+...+Cnn​)+1⋅Cn1​+2⋅Cn2​+...+n⋅Cnn​

Сума у дужках дорівнює 2n. Якщо у формулі бінома Ньютона

(a+b)n=i=0∑n​Cni​aibn−i

покласти a=b=1, то отримаємо співвідношення: (1+1)n=∑i=0n​Cni​1i1n−i, або

2n=i=0∑n​Cni​=Cn0​+Cn1​+...+Cnn​

Обчислимо залишок суми:

1⋅Cn1​+2⋅Cn2​+...+n⋅Cnn​=
1⋅1!⋅(n−1)!n!​+2⋅2!⋅(n−2)!n!​+...+n⋅n!⋅(n−n)!n!​=
n\cdот(0!⋅(n−1)!(n−1)!​+1!⋅(n−2)!(n−1)!​+...+(n−1)!⋅(n−n)!(n−1)!​)=
n⋅(Cn−10​+Cn−11​+...+Cn−1n−1​)=n⋅2n−1

Таким чином, відповідь дорівнює 2n+n⋅2n−1=(n+2)⋅2n−1.

Приклад

Обчислимо відповідь для n=3. При безпосередньому обчисленні:

1⋅C30​+2⋅C31​+3⋅C32​+4⋅C33​=1⋅1+2⋅3+3⋅3+4⋅1=1+6+9+4=20

При обчисленні за формулою: 5⋅22=20.

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

Читаємо вхідне значення n.

scanf("%lld", &n);

Обчислюємо і виводимо відповідь.

res = (1LL << (n - 1)) * (n + 2);
printf("%lld\n", res);

Реалізація алгоритму – функція

Оголосимо масив для запам'ятовування результатів: dp[n][k]=Cnk​.

int dp[31][31];

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
7 lines
177 bytes

Основна частина програми. Читаємо вхідне значення n.

scanf("%d", &n);
memset(dp, -1, sizeof(dp));

Обчислюємо зазначену суму.

res = 0;
for (i = 0; i <= n; i++)
  res += (i + 1) * Cnk(n, i);

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

printf("%d\n", res);
Eolymp #10668. Шляхи на сітці

У вас є аркуш паперу, і ви обираєте на ньому прямокутник розміром n⋅m. Назвемо цей прямокутник разом з лініями, на яких він знаходиться, сіткою. Починаючи з лівого нижнього кута сітки, ви переміщаєте олівець до правого верхнього кута, стежачи за тим, щоб він залишався на лініях і рухався тільки вправо або вгору. Результат показаний зліва:

Дійсно шедевр, чи не так? Повторюючи процедуру ще раз, ви отримаєте зображення, показане праворуч. Тепер виникає питання: скільки різних витворів мистецтва можна таким чином створити?

Вхідні дані. Два натуральні числа n і m.

Вихідні дані. Виведіть кількість різних витворів мистецтва, які можна створити, використовуючи описану вище процедуру. Можна стверджувати, що це число відповідає 64 — бітовому знаковому цілому числу.

Вхідні дані 1
3 4
Вихідні дані 1
35
Вхідні дані 2
1 1
Вихідні дані 2
2
Відкрити задачу
Розв'язок

Шуканий шлях являє собою ламану, що складається з n+m ланок. З них n ланок повинні бути вертикальними, а решта — горизонтальними. Кількість варіантів вибрати n вертикальних ланок з n+m дорівнює Cn+mn​.

Приклад

Для першого прикладу n=3,m=4. Відповідь дорівнює C73​=3!⋅4!7!​=1⋅2⋅37⋅6⋅5​=35.

Для другого прикладу n=1,m=1. Відповідь дорівнює C21​=1!⋅1!2!​=2.

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

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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;
}
C++
8 lines
177 bytes

Основна частина програми. Читаємо вхідні дані.

scanf("%lld %lld", &n, &m);

Обчислюємо і виводимо відповідь Cn+mn​.

res = Cnk(n + m, n);
printf("%lld\n", res);
Eolymp #4008. Біноміальні коефіцієнти

Гуннар — досить старий і забудькуватий дослідник. Зараз він пише статтю про безпеку в соціальних мережах, яка включає в себе деякі елементи комбінаторики. Він написав програму для обчислення біноміальних коефіцієнтів, щоб перевірити деякі розрахунки.

Біноміальний коефіцієнт Cnk​ — це число

Cnk​=k!⋅(n−k)!n!​

де n і k — невід'ємні числа.

Гуннар використовує свою програму для обчислення Cnk​ і отримав число m як результат. На жаль, оскільки він забудькуватий, він забув числа n і k, які він використовував як вхідні дані. Ці два числа були результатом довгих обчислень, і вони були записані на одному з численних аркушів, що лежать на його столі. Замість того щоб шукати в паперах, він вирішив відновити числа n і k за отриманою відповіддю. Чи можете ви допомогти йому знайти всі такі можливі значення?

Вхідні дані. Перша строка містить кількість тестів, не більше 100. Кожен тест задається в одній строкі і містить ціле число m (2≤m≤1015) — результат програми Гуннара.

Вихідні дані. Для кожного тесту виведіть дві строки. Перша строка повинна містити кількість способів виразити m за допомогою біноміального коефіцієнта. У другій строкі повинні розташовуватись всі пари (n,k), що задовольняють Cnk​=m. Пари слід розташувати за зростанням n, а у випадку рівності за зростанням k. Формат виводу пар наведено в прикладі.

Вхідні дані
2
2
15
Вихідні дані
1
(2,1) 
4
(6,2) (6,4) (15,1) (15,14)
Відкрити задачу
Розв'язок

Якщо Cnk​=m, то Cnn−k​=m. Достатньо знайти розв'язок k≤n/2 і разом з парою (k,n) вивести також пару (k,n–k). При k=n/2 ці дві пари співпадають.

Нехай p — найменше число, для якого C2pp​>m. Тоді очевидно що 0≤k<p.

Зафіксуємо таке k що C2kk​≤m і розглянемо функцію f(n)=Cnk​. Тоді при 2k≤n≤m функція f(n) є монотонно зростаючою. Відповідно можна розв'язати рівняння f(n)=m бінарним пошуком.

Таким чином для розв'язання задачі слід перебрати значення k (0≤k<p) і для кожного такого k розв'язати рівняння Cnk​=m щодо n бінарним пошуком. Знайдене значення n повинно бути цілочисельним.

Приклад

Розглянемо рівняння Cnk​=3003. Зважаючи на те що C126​=924, а C147​=3432, то нам достатньо перебрати 0≤k≤6.

Нехай k=2, розглянемо рівняння Cn2​=3003 або 2n⋅(n−1)​=3003, n⋅(n–1)=6006. Бінарним пошуком в проміжку 4≤n≤3003 знаходимо цілочисельне розв'язання n=78. Зважаючи на те що n=2⋅k, маємо два розв'язки: C782​=C7876​=3003.

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

Шукані пари будемо зберігати у векторі пар res.

vector<pair<long long,long long> > res;

Функція Cnk обчислює значення біноміального коефіцієнта Cnk​.

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 (ми шукаємо розв'язок рівняння Cnk​=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 до тих пір поки C2kk​≤m.

  for(k = 0; Cnk(2*k,k) <= m; k++)
  {

Шукаємо значення n (2k≤n≤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;
    }

Якщо Clok​=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));
    }
  }
C++
7 lines
144 bytes

Сортуємо пари.

  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. Між біноміальними коефіцієнтами має місце співвідношення:

Cn0​<Cn1​<...<Cn⌊n/2⌋​≥Cn⌊n/2⌋+1​>...>Cnn​

Теорема 2. C2nn​<4n. Слід із того, що ∑k=02n​C2nk​=(1+1)2n=22n=4n

Теорема 3. C2nn​>2n+14n​. Слід із того, що ∑k=02n​C2nk​=4n, а самих коефіцієнтів 2n+1. Тому більший з коефіцієнтів буде більшим 2n+14n​.

Теорема 4. Формула Стірлінга. n!≈2πn​(en​)n.

Більш точне наближення має вигляд:

n!≈2πn​(en​)n(1+12n1​+288n21​−51849n3139​−2488320n4571​)

Теорема 5.

C2nn​≈(2πn​(en​)n)24πn​(e2n​)2n​=піn​4n​

Теорема 6.

Cnk​=k!(n−k)!n!​=1⋅2⋅3⋅...⋅kn⋅(n−1)⋅(n−2)⋅...⋅(n−k+1)​=
k!n!​⋅(1−n1​)⋅(1−n2​)⋅...⋅(1−nk−1​)=
k!n!​⋅eln(1−n1​)+ln(1−n2​)+...+ln(1−nk−1​)≤(скористаємося нерівністю ln(1−x)≤−x)
k!n!​⋅e−n1​−n2​−...−nk−1​=k!n!​⋅e−2nk⋅(k−1)​

Список використаних задач

  • 318. Біноміальні коефіцієнти

  • 3260. Скільки?

  • 4008. Біноміальні коефіцієнти

  • 5329. Вечірка

  • 9892. C0n + … + Cnn

  • 10668. Шляхи на сітці

  • 11271. Сума квадратів Cnk

  • 11550. x1 + … + xk = n

  • 11551. x1 + … + xk = n (2)

  • 11488. Біноміальна сума


37

Коментарі

Завантаження

Секундочку, отримую дані з серверу

Покищо нічого

Будьте першим, хто розпочне обговорення!
Увійти