Basecamp
    Главная
    Задачи
    Соревнования
    Курсы
    Рейтинг
    Посты
    Store
    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⋅(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. Биномиальная сумма


36

Комментарии

Загрузка

Момент, получаем данные с сервера

Пока что ничего

Будьте первым, кто начнет обсуждение!
Войти