PrevNext
Rare
 0/8

Inclusion-Exclusion Principle

Author: Mihnea Brebenel

Contributor: Rameez Parwez

The inclusion-exclusion principle is a counting technique that generalizes the formula for computing the size of union of n finite sets.

Edit This Page

Resources

Resources
cp-algo Well-covered article
CF Good Explanation
WikipediaWiki

Introduction

The inclusion-exclusion principle relates to finding the size of the union of some sets.

Verbally it can be stated as following:

Sum the sizes of the sets separately, subtract the sizes of all pairwise intersections of the sets, add back the sizes of intersections of triples of the sets, subtract the size of quadruples of the sets, ...

The mathematical identity of the above is:

i=1nAi=i=1nAi1i<jnAiAj+1i<j<knAiAjAk+(1)n1A1An\left| \bigcup_{i=1}^n A_i \right| = \sum_{i=1}^n|A_i| - \sum_{1\leq i<j\leq n} |A_i \cap A_j| + \sum _{1\leq i<j<k\leq n}|A_i \cap A_j \cap A_k| - \cdots + (-1)^{n-1} | A_1 \cap \cdots \cap A_n |

Written in a compact form:

i=1nAi=0J{1,2,...,n}(1)J1jJAj\bigg|\bigcup_{i=1}^nA_i \bigg|= \sum_{0 \neq J \in \{1, 2,...,n\} } (-1)^{|J|-1} \bigg| \bigcap_{j \in J} A_j \bigg|

Mobius Function

The Mobius function is a multiplicative function that comes in handy when dealing with inclusion-exclusion technique and divisors-related problems. It has values in {1,0,1}\{-1, 0, 1\} depending on number's factorization.

μ(n)={1if n is 1,0if n has a squared prime factor,(1)kif n is a product of k distinct prime factors.\mu(n)=\begin{cases} 1 & \text{if $n$ is $1$},\\ 0 & \text{if $n$ has a squared prime factor},\\ (-1)^k & \text{if $n$ is a product of $k$ distinct prime factors}. \end{cases}

Below you can see the first 1919 values of μ(n)\mu(n):

n12345678910111213141516171819
μ(n)\mu(n)1-1-10-11-1001-10-1110-10-1

Let's take a look at how the mobius function can be precomputed with a slightly modified sieve.

C++

mobius[1] = -1;
for (int i = 1; i < VALMAX; i++) {
if (mobius[i]) {
mobius[i] = -mobius[i];
for (int j = 2 * i; j < VALMAX; j += i) { mobius[j] += mobius[i]; }
}
}

Applications

SQFREE

Focus Problem – try your best to solve this problem before continuing!

Explanation

A perfect application for inclusion-exclusion principle and mobius function. In this particular case the set AiA_i - previously mentioned in the tutorial section - denotes how many numbers are divisible with i2i^2 and we're asked to find out i=1nAi\bigg| \bigcup_{i=1}^{\sqrt{n}} A_i \bigg|. The precomputed mobius array tells whether to add or subtract AiA_i.

Implementation

Time Complexity: O(VlogV+Tn)\mathcal{O}(V \log V + T \cdot \sqrt{n}), where V=1e7V = 1e7

C++

#include <iostream>
#include <vector>
using namespace std;
const int VALMAX = 1e7;
int mobius[VALMAX];
int main() {

Cowpatibility

Focus Problem – try your best to solve this problem before continuing!

View Internal Solution

Explanation

In this particular case the set AiA_i - previously mentioned in the tutorial section - denotes how many pairs of cows have at least ii ice cream flavors in common. From the total number of pairs subtract the union of AiA_i. The global answer is:

n(n1)2i=15Ai\frac{n \cdot(n-1)}{2}- \bigg| \bigcup_{i=1}^{5} A_i \bigg|

Implementation

Time Complexity: O(NlogN)\mathcal{O}(N \log N)

C++

#include <bits/stdc++.h>
using namespace std;
int main() {
ifstream in("cowpatibility.in");
int n;
in >> n;
map<vector<int>, int> subsets;
for (int i = 1; i <= n; i++) {

Python

Warning!

Due to Python's constant factor, the below solution TLEs on a couple test cases.

from collections import defaultdict
with open("cowpatibility.in", "r") as read:
n = int(read.readline().strip())
subsets = defaultdict(int)
for _ in range(n):
flavors = list(map(int, read.readline().strip().split()))
flavors.sort()

The number of strings that match a certain pattern

Focus Problem – try your best to solve this problem before continuing!

Explanation 1

A dynamic programming approach with bitmasking would look like this:

dp[i][mask]=the number of strings of length i that match all the patterns in set, but none other patterns. dp[i][mask] = \text{the number of strings of length i that match all the patterns in set, but none other patterns. } The recurrence is:

dp[i][mask&j]=dp[i1][j] where j is a set of patterns that match charachter c at position idp[i][mask \& j]=dp[i-1][j]\text{ where j is a set of patterns that match charachter c at position i}

The following code illustrates this:

Implementation

Time Complexity: O(MN2N)\mathcal{O}(M \cdot N \cdot 2^N), where MM is size of each strings in patterns and NN is the size of patterns.

C++

const int MOD = 1000003;
int howMany(vector<string> patterns, int k) {
int n = patterns.size();
int m = patterns[0].size();
vector<vector<int>> dp(50, vector<int>(1 << n));
for (int i = 0; i < m; i++) {
for (char c = 'a'; c <= 'z'; c++) {
int mask = 0;
for (int j = 0; j < n; j++) {

Explanation 2

The problem can also be solved using the inclusion exclusion principle.

An important observation is that we can easily count the strings that satisfy some specific patterns. Simply iterate through the positions of all patterns. If all the patterns contain ?? then we can use any letter from aa to zz giving us 2626 solution, otherwise we can only put the fixed letter contained by a pattern. The answer is the product.

Iterate over subsets - denoted by AA - of patterns consisting of exactly kk strings. For this specific subset count the number of string that can only match all the patterns in subset AA. Apply the inclusion-exclusion principle over all supersets BB such that ABA \subset B.

solve(A)=BA(1)Bkf(B)solve(A) = \sum_{B \supseteq A} (-1)^{|B|-k} \cdot f(B)

f(B)f(B) denotes the number of strings matching at least set BB

The global answer is:

ans=A:A=ksolve(A)ans = \sum_{A:|A|=k} solve(A)

Implementation

Time Complexity: O((Nk)2NkMN)\mathcal{O}(\binom{N}{k} \cdot 2^{N - k} \cdot M \cdot N)

C++

const int MOD = 1000003;
int howMany(vector<string> patterns, int k) {
int n = patterns.size();
int m = patterns[0].size();
int ans = 0;
for (int mask = 0; mask < (1 << n); mask++) {
// subsets with exactly k patterns matters
if (__builtin_popcount(mask) != k) { continue; }
// iterate over all superset of current subset mask
StatusSourceProblem NameDifficultyTags
SPOJNormal
Show TagsDivisors, PIE
SPOJNormal
Show TagsDivisors, PIE
CSESNormal
Show TagsPIE
CSESNormal
Show TagsDivisors, PIE
CSESNormal
Show TagsPIE

Module Progress:

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!

PrevNext