MAXimal | |
добавлено: 25 Aug 2011 13:55 Содержание [скрыть] Принцип включений-исключенийПринцип включений-исключений — это важный комбинаторный приём, позволяющий подсчитывать размер каких-либо множеств, или вычислять вероятность сложных событий. Формулировки принципа включений-исключенийСловесная формулировкаПринцип включений-исключений выглядит следующим образом: Чтобы посчитать размер объединения нескольких множеств, надо просуммировать размеры этих множеств по отдельности, затем вычесть размеры всех попарных пересечений этих множеств, прибавить обратно размеры пересечений всевозможных троек множеств, вычесть размеры пересечений четвёрок, и так далее, вплоть до пересечения всех множеств. Формулировка в терминах множествВ математической форме приведённая выше словесная формулировка выглядит следующим образом: Её можно записать более компактно, через сумму по подмножествам. Обозначим через Эту формулу приписывают Муавру (Abraham de Moivre). Формулировка с помощью диаграмм ВеннаПусть на диаграмме отмечены три фигуры Тогда площадь объединения Аналогичным образом это обобщается и на объединение Формулировка в терминах теории вероятностейЕсли Эту сумму также можно записать в виде суммы по подмножествам множества Доказательство принципа включений-исключенийДля доказательства удобно пользоваться математической формулировкой в терминах теории множеств: где Нам нужно доказать, что любой элемент, содержащийся хотя бы в одном из множеств Рассмотрим произвольный элемент Заметим, что:
Таким образом, нам надо посчитать такую сумму биномиальных коэффициентов: Проще всего посчитать эту сумму, сравнив её с разложением в бином Ньютона выражения Видно, что при Применения при решении задачПринцип включений-исключений сложно хорошо понять без изучения примеров его применений. Сначала мы рассмотрим три простые задачи "на бумажке", иллюстрирующие применение принципа, затем рассмотрим более практические задачи, которые трудно решить без использования принципа включений-исключений. Особо следует отметить задачу "поиск числа путей", поскольку в ней демонстрируется, что принцип включений-исключений может иногда приводить к полиномиальным решениям, а не обязательно экспоненциальным. Простая задачка о перестановкахСколько есть перестановок чисел от Посчитаем число "плохих" перестановок, т.е. таких, у которых первый элемент Обозначим через Проведя несложные комбинаторные вычисления, получаем, что это равно: Отнимая это число от общего числа перестановок Простая задачка о (0,1,2)-последовательностяхСколько существует последовательностей длины Снова перейдём к обратной задаче, т.е. будем считать число последовательностей, в которых не присутствует хотя бы одно из чисел. Обозначим через Размеры каждого из Вспоминая, что мы решали обратную задачу, получаем итоговый ответ: Количество целочисленных решений уравненияДано уравнение: где все Требуется посчитать число решений этого уравнения. Забудем сначала про ограничение Посчитаем теперь по формуле включений-исключений число "плохих" решений, т.е. таких решений уравнения, в которых один или более Обозначим через Аналогично, мощность пересечения двух множеств Мощность каждого пересечения трёх и более множеств равна нулю, поскольку Объединяя всё это в формулу включений-исключений и учитывая, что мы решали обратную задачу, окончательно получаем ответ: Количество взаимно простых чисел в заданном отрезкеПусть даны числа Сразу перейдём к обратной задаче — посчитаем количество не взаимно простых чисел. Рассмотрим все простые делители числа Сколько чисел в отрезке Однако если мы просто просуммируем эти числа, то получим неправильный ответ — некоторые числа будут просуммированы несколько раз (те, которые делятся сразу на несколько Например, можно за Итоговая реализация для подсчёта количества взаимно простых чисел: int solve (int n, int r) { vector<int> p; for (int i=2; i*i<=n; ++i) if (n % i == 0) { p.push_back (i); while (n % i == 0) n /= i; } if (n > 1) p.push_back (n); int sum = 0; for (int msk=1; msk<(1<<p.size()); ++msk) { int mult = 1, bits = 0; for (int i=0; i<(int)p.size(); ++i) if (msk & (1<<i)) { ++bits; mult *= p[i]; } int cur = r / mult; if (bits % 2 == 1) sum += cur; else sum -= cur; } return r - sum; } Асимптотика решения составляет Количество чисел в заданном отрезке, кратных хотя бы одному из заданных чиселДаны Алгоритм решения практически совпадает с предыдущей задачей — делаем формулу включений-исключений над числами Таким образом, решение сводится к тому, чтобы за Количество строк, удовлетворяющих заданному числу паттерновДано Заметим вначале, что мы можем легко посчитать число строк, удовлетворяющих сразу всем указанным паттернам. Для этого надо просто "пересечь" эти паттерны: посмотреть на первый символ (во всех ли паттернах на первой позиции стоит вопрос, или не во всех — тогда первый символ определён однозначно), на второй символ, и т.д. Научимся теперь решать первый вариант задачи: когда искомые строки должны удовлетворять ровно Для этого переберём и зафксируем конкретное подмножество где Если мы просуммируем Однако тем самым мы получили решение за время порядка Решение можно ускорить, заметив, что в разных Перевернём формулу включений-исключений и будем вести суммирование по Решение получилось с асимптотикой Перейдём теперь ко второму варианту задачи: когда искомые строки должны удовлетворять как минимум Понятно, мы можем просто воспользоваться решением первого варианта задачи и просуммировать ответы от Таким образом, в итоговой формуле перед Заглянув в Грэхема (Грэхем, Кнут, Паташник. "Конкретная математика" [1998] ), мы видим такую известную формулу для биномиальных коэффициентов: Применяя её здесь, получаем, что вся эта сумма биномиальных коэффициентов сворачивается в: Таким образом, для этого варианта задачи мы также получили решение с асимптотикой Количество путейЕсть поле Предполагаем, что размеры Для решения сразу в целях удобства отсортируем препятствия в том порядке, в каком мы можем их обойти: т.е., например, по координате Также сразу научимся решать задачу без препятствий: т.е. научимся считать число способов дойти от одной клетки до другой. Если по одной координате нам надо пройти Теперь чтобы посчитать число способов дойти от одной клетки до другой, избежав всех препятствий, можно воспользоваться формулой включений-исключений: посчитаем число способов дойти, наступив хотя бы на одно препятствие. Для этого можно, например, перебрать подмножество тех препятствий, на которые мы точно наступим, посчитать число способов сделать это (просто перемножив число способов дойти от стартовой клетки до первого из выбранных препятствий, от первого препятствия до второго, и так далее), и затем прибавить или отнять это число от ответа, в соответствии со стандартной формулой включений-исключений. Однако это снова будет неполиномиальное решение — за асимптотику Решать будем динамическим программированием: научимся вычислять числа Если мы на секунду забудем про все препятствия и просто посчитаем число путей из клетки Таким образом, значение Число взаимно простых четвёрокДано Будем решать обратную задачу — посчитаем число "плохих" четвёрок, т.е. таких четвёрок, в которых все числа делятся на число Воспользуемся формулой включений-исключений, суммируя количество четвёрок, делящихся на делитель где Чтобы посчитать функцию Таким образом, с помощью формулы включений-исключений мы суммируем количество четвёрок, делящихся на простые числа, затем отнимаем число четвёрок, делящихся на произведение двух простых, прибавляем четвёрки, делящиеся на три простых, и т.д. Число гармонических троекДано число
Во-первых, сразу перейдём к обратной задаче — т.е. посчитаем число негармонических троек. Во-вторых, заметим, что в любой негармонической тройке ровно два её числа находятся в такой ситуации, что это число взаимно просто с одним числом тройки и не взаимно просто с другим числом тройки. Таким образом, количество негармонических троек равно сумме по всем числам от Теперь всё, что нам осталось для решения задачи — это научиться считать для каждого числа в отрезке Поэтому нам понадобится более быстрое решение, которое подсчитывает ответы для всех чисел из отрезка Для этого можно реализовать такую модификацию решета Эратосфена:
Реализация: int n; bool good[MAXN]; int deg[MAXN], cnt[MAXN]; long long solve() { memset (good, 1, sizeof good); memset (deg, 0, sizeof deg); memset (cnt, 0, sizeof cnt); long long ans_bad = 0; for (int i=2; i<=n; ++i) { if (good[i]) { if (deg[i] == 0) deg[i] = 1; for (int j=1; i*j<=n; ++j) { if (j > 1 && deg[i] == 1) if (j % i == 0) good[i*j] = false; else ++deg[i*j]; cnt[i*j] += (n / i) * (deg[i]%2==1 ? +1 : -1); } } ans_bad += (cnt[i] - 1) * 1ll * (n-1 - cnt[i]); } return (n-1) * 1ll * (n-2) * (n-3) / 6 - ans_bad / 2; } Асимптотика такого решения составляет Число перестановок без неподвижных точекДокажем, что число перестановок длины и приблизительно равно числу: (более того, если округлить это выражение к ближайшему целому — то получится в точности число перестановок без неподвижных точек) Обозначим через Воспользуемся теперь формулой включений-исключений, чтобы посчитать число перестановок хотя бы с одной неподвижной точкой. Для этого нам надо научиться считать размеры множеств-пересечений поскольку если мы знаем, что число неподвижных точек равно Подставляя это в формулу включений-исключений и учитывая, что число способов выбрать подмножество размера Тогда число перестановок без неподвижных точек равно: Упрощая это выражение, получаем точное и приблизительное выражения для количества перестановок без неподвижных точек: (поскольку сумма в скобках — это первые В заключение стоит отметить, что аналогичным образом решается задача, когда требуется, чтобы неподвижных точек не было среди Задачи в online judgesСписок задач, которые можно решить, используя принцип включений-исключений:
Литература
|