Competitive Programming Notebook
Competitive Programming Notebook
Salil Gokhale
This work may be distributed and/or modified under the conditions of the LaTeX Project Public
License, either version 1.3 of this license or (at your option) any later version. The latest version
of this license is in http://www.latex-project.org/lppl.txt and version 1.3 or later is part of all
distributions of LaTeX version 2005/12/01 or later.
ii
Contents
I Basic techniques 3
iii
3.4 Common O (n log n) Algorithms 13
3.4.1 Merge Sort 13
3.4.2 Quick Sort 14
3.4.3 Heap Sort 16
3.4.4 Counting Sort 16
3.5 Comparison of Sorting Algorithms 16
3.6 Sorting in C++ 16
Chapter 6 Searching 29
6.1 Binary Search 29
6.1.1 Binary Search in C++ STL 31
6.2 Two Pointer Technique 31
6.3 Sliding window minimum 32
iv
II Dynamic Programming 35
v
IV Graph Traversals 63
Chapter 12 Theory 77
vi
Chapter 15 Floyd–Warshall algorithm 89
15.1 Pseudo-code 90
15.2 Example 90
15.3 Implementation 92
15.3.1Detecting Negative Cycles 93
VI Mathematics 95
vii
VII Question Bank 113
viii
3. 14159265358979323846264338327950288419716
93993751058209749445923078164062862089986280348
2534211706798214808651328230664709384460955058223
17253594081284811174502841027019385211055596446229
48954930 381 964
4 2 8 8 1 0 9 7 5 6 6
5 9 3 3 4 4 6 1 2 8
4756 4 8 2 3 3 7
8 6 7 8 3 1 6 5 2
7 1 2 0 1 9 0 9 1
4 5 6 4 8 5 6 6
9234 6034
8610 4543
2 6 6 4 8 2 1 3
3 9 3 6 0 7 2 6
0 2 4 9 14127
37245 87006
60631 5 5 8 8 1
74881 5 2 0 9 2
0 9 6 2 8 292540
9 1 7 1 5 364367
892590 360011
330530 5 4 8 8 2 0
466521 3841469
5 1 9 4 1 5 1160943
3057270 3657595
9195309 2 1 8 6 1 1 7
3819326 11793105
1185480 74462379
96274956 73518857
5272489 1227938
18301 19491
π for good luck!
1
2
Part I
Basic techniques
3
Chapter 1
Code Template
#include<bits/stdc++.h>
using namespace std;
//macro for debugging. Run watch(n) if you want to debug n;
#define watch(x) cerr << "\n" << (#x) << " is " << (x) << endl
const double PI = 3.141592653589793238463; //value of pi
const int MOD = 1000000007; //used in many problems
/*
$alil03
URL: url of problem
Solution Begins here
*/
int main()
{
//FAST I/O
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
5
6
Chapter 2
C++ Tricks
7
2.1.5 Iota
It fills a vector (or some container) with increasing values starting with x
8
2.2 Input and output
Input and output is sometimes a bottleneck in the program. The following lines at
the beginning of the code make input and output more efficient:
ios::sync_with_stdio(0);
cin.tie(0);
Sometimes the program should read a whole line from the input, possibly containing
spaces. This can be accomplished by using the getline function:
string s;
getline(cin, s);
This loop reads elements from the input one after another, until there is no more
data available in the input.
In some contest systems, files are used for input and output. An easy solution for
this is to write the code as usual using standard streams, but add the following lines
to the beginning of the code:
After this, the program reads the input from the file ”input.txt” and writes the output
to the file ”output.txt”.
9
10
Chapter 3
Sorting Algorithms
1 3 8 2 9 2 5 6
1 2 2 3 5 6 8 9
Comparison Sorts
Comparison sorts compare elements at each step of the algorithm to determine if one
element should be to the left or right of another element.
Comparison sorts are usually more straightforward to implement than integer sorts,
but comparison sorts are limited by a lower bound of O (n log n), meaning that, on
average, comparison sorts cannot be faster than O (n log n).
Integer Sorts
Integer sorts are sometimes called counting sorts (though there is a specific integer
sort algorithm called counting sort). Integer sorts do not make comparisons. Integer
sorts determine for each element x - how many elements are less than x. For example,
if there are 14 elements that are less than x, then x will be placed in the 15th slot.
11
This information is used to place each element into the correct slot immediately, so
there is no need to rearrange lists.
3.2.2 Stability
Stable sorting algorithms maintain the relative order of records with equal keys (i.e.
values). That is, a sorting algorithm is stable if whenever there are two records R
and S with the same key and with R appearing before S in the original list, R will
appear before S in the sorted list.
3.2.3 Adaptability
It means whether or not the presortedness of the input affects the running time.
Algorithms that take this into account are known to be adaptive.
12
2 2 2 2 4 4 4 4 4 4
4 4 4 4 2 2 3 3 3 3
1 1 3 3 3 3 2 2 2 2
3 3 1 1 1 1 1 1 1 1
Example of Bubble Sort
Start 4 3 2 1 Iteration C 2 3 4 1
Iteration A 4 3 2 1 2 3 1 4
Iteration B 3 4 2 1 2 1 3 4
Finished 1 2 3 4
3 2 4 1
Insertion Sort Example
13
5. Merge the sorted subarrays array[ a . . . k ] and array[k + 1 . . . b] into a sorted
subarray array[ a . . . b].
Merge sort is an efficient algorithm, because it halves the size of the subarray at
each step. The recursion consists of O (log n) levels, and processing each level takes
O(n) time. Merging the subarrays array[ a . . . k] and array[k + 1 . . . b] is possible in
linear time, because they are already sorted.
For example, consider sorting the following array:
1 3 6 2 8 2 5 9
1 3 6 2 8 2 5 9
1 3 6 2 8 2 5 9
1 3 6 2 8 2 5 9
MERGING BEGINS
1 3 6 2 8 2 5 9
1 3 2 6 2 8 5 9
1 2 3 6 2 5 8 9
1 2 2 3 5 6 8 9
1. Pick a pivot element, A[q]. Picking a good pivot is the key for a fast imple-
mentation of quicksort; however, it is difficult to determine what a good pivot
might be.
2. Partition, or rearrange, the array into two subarrays: A[ p, ..., q − 1] such that
all elements are less than A[q], and A[q + 1, ..., r ] such that all elements are
greater than or equal to A[q].
14
Conquer:
1. Sort the subarrays A[ p, ..., q − 1] and A[q + 1, ..., r ] recursively with quicksort.
Combine:
1. No work is needed to combine the arrays because they are already sorted.
3 7 8 5 2 1 9 5 4
3 7 8 5 2 1 9 5 4
3 5 8 5 2 1 9 4 7
3 9 8 5 2 1 4 5 7
3 1 8 5 2 4 9 5 7
3 1 2 5 4 8 9 5 7
3 1 2 4 5 8 9 5 7
3 1 2 5 8 9 5 7
1 2 3 5 5 9 7 8
5 5 7 9 8
5 5 9 8
8 9
1 2 3 4 5 5 7 8 9
Quicksort Example
15
3.4.3 Heap Sort
Heapsort is a much more efficient version of selection sort. It also works by determining
the largest (or smallest) element of the list, placing that at the end (or beginning) of
the list, then continuing with the rest of the list, but accomplishes this task efficiently
by using a data structure called a heap, a special type of binary tree.
vector<int> v = {4,2,5,3,5,8,3};
sort(v.begin(),v.end());
16
After the sorting, the contents of the vector will be [2, 3, 3, 4, 5, 5, 8]. The default
sorting order is increasing, but a reverse order is possible as follows:
sort(v.rbegin(),v.rend());
string s = "monkey";
sort(s.begin(), s.end());
Sorting a string means that the characters of the string are sorted. For example, the
string ”monkey” becomes ”ekmnoy”.
Integer containers can be sorted in decreasing order as follows:
17
18
Chapter 4
Bit Manipulation
4.1 Tricks
4.1.1 Multiply and divide by 2i
if (num & 1)
cout << "ODD";
else
cout << "EVEN";
a ^= b;
b ^= a;
a ^= b;
19
4.1.4 Compute XOR from 1 to n (direct method)
int computeXOR(int n)
{
if (n % 4 == 0)
return n;
if (n % 4 == 1)
return 1;
if (n % 4 == 2)
return n + 1;
else
return 0;
}
20
4.1.5 Check if a number is a power of 2
bool poweroftwo(int x)
{
return x & (x-1) == 0;
}
ch |= ’ ’; //Upper to Lower
ch &= ’_’ ; //Lower to Upper
int logarithm(int x)
{
int res = 0;
while (x >>= 1)
res++;
return res;
}
4.2.1 Application
Check if kth bit is set
The kth bit of a number is one exactly when x (1<<k ) is not zero. The following
code prints the bit representation of an int number x:
21
Set the kth bit
T = (S & (-S))
//T is a power of two with only one bit set which is the LSB.
Trivia
The formula x & ( x − 1) sets the last one bit of x to zero. The formula x | ( x − 1)
inverts all the bits after the last one bit.
22
4.3 C++ Special Functions
The g++ compiler provides the following functions for counting bits:
While the above functions only support int numbers, there are also long long
versions of the functions available with the suffix ll like __builtin_popcountll( x ).
00000000000000000000000100011010,
int x = 0;
x |= (1<<1);
x |= (1<<3);
x |= (1<<4);
x |= (1<<8);
cout << __builtin_popcount(x) << "\n"; // 4
23
Then, the following code prints all elements that belong to the set:
Solution
Naive Solution of time complexity O (n3 ):
Go through all O(n2 ) pairs of rows and for each pair ( a, b) calculate the
number of columns that contain a black square in both rows in O(n) time.
The following code assumes that color[y][ x ] denotes the color in row y and
column x:
int count = 0;
for (int i = 0; i < n; i++) {
if (color[a][i] == 1 && color[b][i] == 1) count++;
}
Then, those columns account for count(count − 1)/2 subgrids with black
corners, because we can choose any two of them to form a subgrid.
24
Solution
Optimized solution:
Divide the grid into blocks of columns such that each block consists of N
consecutive columns. Then, each row is stored as a list of N-bit numbers that
describe the colors of the squares. Now we can process N columns at the same
time using bit operations. In the following code, color[y][k] represents a block
of N colors as bits.
int count = 0;
for (int i = 0; i <= n/N; i++) {
count += __builtin_popcount(color[a][i]&color[b][i]);
}
25
26
Chapter 5
Brute-Force Algorithms
Example. For example, the subsets of {0, 1, 2} are ∅, {0}, {1}, {2}, {0, 1}, {0, 2},
{1, 2} and {0, 1, 2}.
Solution
We can use the bit representation of numbers to generate subsets.
Let’s say, set s has n elements. We will use the bits of numbers to show the
presence of element in set. If x th bit in number is SET, then x th element in s is
present in current subset. We will loop from 0 to 2n − 1, and for each number,
we will check among the first n bits, the SET bits in it and take corresponding
elements. In each iteration, we will have one subset.
27
5.2 Generating Permutations
Problem: Print all the permutations of a set of size n.
Example. For example, the permutations of {0, 1, 2} are (0, 1, 2), (0, 2, 1), (1, 0, 2),
(1, 2, 0), (2, 0, 1) and (2, 1, 0).
Solution
We can use the built-in C++ function next_permutation which rearranges the
array into the next lexicographically greater permutation. It returns 1 if this is
possible and 0 otherwise.
28
Chapter 6
Searching
Binary search is the most popular search algorithm. It is efficient and also one of the
most commonly used techniques that is used to solve problems.
Problem: Given a sorted array A[] of n elements, write a function to search a given
element x in A[].
Constraints: The array contains about 1010 elements. Each element is less than
109
Solution
Naive Approach: A simple approach is to do a linear search(checking every
element). The time complexity would be O (n)
Binary Search: Search a sorted array by repeatedly dividing the search interval
in half. Begin with an interval covering the whole array. If the value of the
search key is less than the item in the middle of the interval, narrow the interval
to the lower half. Otherwise narrow it to the upper half. Repeatedly check
until the value is found or the interval is empty.
Since we are halving the array at each step, the time complexity becomes
O (log n)
29
int search(int low,int high,int key)
{
while(low<=high)
{
int mid=(low+high)/2;
if(a[mid]<key)
{
low=mid+1; //search upper half
}
else if(a[mid]>key)
{
high=mid-1; //search lower half
}
else
{
return mid; //key found
}
}
return -1; //key not found
}
30
6.1.1 Binary Search in C++ STL
Two pointer is really an easy and effective technique which is typically used for
searching pairs in a sorted array. Binary search is a kind of optimisation on the
number of trials taken to reach the optimal position and so is the two pointer
technique. The approach relies on the sequence following one specific property on
which our pointers can move.
We will explain this technique using an example problem.
Problem: Given a sorted array A, having N integers. You need to find any pair(i, j)
having sum as given number X.
Constraints: Array A contains about 105 integers with each having values around
109 .
Solution
Naive Solution: We iterate over every i and j and find out the pair which
sums up to X. This approach is to slow because it leads to a time complexity
of O (n2 )
2 Pointer Technique: Now let’s see how the two pointer technique works.
We take two pointers, one representing the first element and other representing
the last element of the array, and then we add the values kept at both the
pointers. If their sum is smaller than X then we shift the left pointer to right
or if their sum is greater than X then we shift the right pointer to left, in order
to get closer to the sum. We keep moving the pointers until we get the sum
as X or the left pointer meets the right pointer and we don’t find the value X.
The time complexity is O (n) if the array is already sorted.
31
bool isPairSum(A[], N, X)
{
int i = 0; //left pointer
int j = N - 1; //right pointer
while (i < j)
{
// If we find a pair
if (A[i] + A[j] == X)
return true;
2 1 4 5 3 4 1 2
32
Suppose that the size of the sliding window is 4. At the first window position, the
smallest value is 1:
2 1 4 5 3 4 1 2
1 4 5
Then the window moves one step right. The new element 3 is smaller than the
elements 4 and 5 in the queue, so the elements 4 and 5 are removed from the queue
and the element 3 is added to the queue. The smallest value is still 1.
2 1 4 5 3 4 1 2
1 3
After this, the window moves again, and the smallest element 1 does not belong
to the window anymore. Thus, it is removed from the queue and the smallest value
is now 3. Also the new element 4 is added to the queue.
2 1 4 5 3 4 1 2
3 4
The next new element 1 is smaller than all elements in the queue. Thus, all
elements are removed from the queue and it will only contain the element 1:
2 1 4 5 3 4 1 2
1
Finally the window reaches its last position. The element 2 is added to the queue,
but the smallest value inside the window is still 1.
2 1 4 5 3 4 1 2
1 2
Since each array element is added to the queue exactly once and removed from
the queue at most once, the algorithm works in O(n) time.
33
34
Part II
Dynamic Programming
35
Chapter 7
Common DP Problems
j
∑ A[ x ]
x =i
is as large as possible
Solution
This is called Kadane’s algorithm.
The key idea of Kadane’s algorithm is to keep a running sum of the integers
seen so far and greedily reset that to 0 if the running sum dips below 0. This
is because re-starting from 0 is always better than continuing from a negative
running sum.
37
7.2 Rod Cutting
Problem (DP): Given a rod of length n inches and an array of prices that contains
prices of all pieces of size smaller than n. Determine the maximum value obtainable
by cutting up the rod and selling the pieces.
Example. If length of the rod is 8 and the values of different pieces are given as
following, then the maximum obtainable value is 22 (by cutting in two pieces of
lengths 2 and 6). Price array = [1,5,8,9,10,17,17,20]
Solution
We fix the last piece of rod with length i and iterate over i
38
7.3 Longest Increasing Subsequence(LIS)
Problem (DP): Find the length of the longest subsequence of a given sequence such
that all elements of the subsequence are sorted in increasing order.
Example. Sum of LIS for [10, 22, 9, 33, 21, 50, 41, 60, 80] is 6 and LIS is
[10, 22, 33, 50, 60, 80].
Solution
Let arr[0..n-1] be the input array and L(i) be the length of the LIS ending
at index i such that arr[i] is the last element of the LIS.
Then, L(i) can be recursively written as:
L(i) = 1 + max(L(j)) where 0 ≤ j ≤ i and arr[j] < arr[i]; or
L(i) = 1, if no such j exists.
To find the LIS for a given array, we need to return max(L(i)) where 0 ≤ i ≤ n.
39
7.4 Longest Common Subsequence(LCS)
Problem: Given two sequences, find the length of longest subsequence present in
both of them. A subsequence is a sequence that appears in the same relative order,
but not necessarily contiguous
Example. LCS for input Sequences “ABCDGH” and “AEDFHR” is “ADH” of length 3.
Example. LCS for input Sequences “AGGTAB” and “GXTXAYB” is “GTAB” of length 4.
Solution
40
7.5 Tiling Problem
Problem: Given a 2 × n board and tiles of size 2 × 1, count the number of ways to
tile the given board using the 2 × 1 tiles. A tile can either be placed horizontally i.e.,
as a 1 × 2 tile or vertically i.e., as 2 × 1 tile.
Example. Input n = 3
Output: 3
Explanation:
We need 3 tiles to tile the board of size 2 x 3.
We can tile the board using following ways
1) Place all 3 tiles vertically.
2) Place first tile vertically and remaining 2 tiles horizontally.
3) Place first 2 tiles horizontally and remaining tiles vertically
The first solution is shown below
Example. Input n = 4
Output: 5
Explanation:
For a 2 x 4 board, there are 5 ways
1) All 4 vertical
2) All 4 horizontal
3) First 2 vertical, remaining 2 horizontal
4) First 2 horizontal, remaining 2 vertical
5) Corner 2 vertical, middle 2 horizontal
The third solution is shown below:
41
Solution
Tiling Problem is nothing but Fibonacci sequence
Method 1:
int fib(int n)
{
//Space optimized Fibonacci
int a = 1, b = 1, c, i;
if( n == 1 || n==2)
return n;
for (i = 2; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return b;
}
int fib(int n)
{
double phi = (1 + sqrt(5)) / 2;
return round(pow(phi, n) / sqrt(5));
}
42
7.6 Coin - 1
Problem: Given a value n, if we want to make change for n cents, and we have
infinite supply of each of S = [S1 , S2 , .., Sm ] valued coins, how many ways can we
make the change? The order of coins does not matter.
Solution
Recursive Equation:
x=0 1
change( x ) = {
x > 0 ∑c∈coins change( x − c)
43
7.7 Coin - 2
Problem: Given a value n, if we want to make change for n cents, and we have
infinite supply of each of C = [ C1 , C2 , .., Cm ] valued coins, what is the minimum
number of coins to make the change?
Solution
// Base case
table[0] = 0; //Again first hand experience :)
44
7.8 0-1 Knapsack Part 1
Problem: Given n items, each with its own value Vi and weight Wi , ∀i ∈ [0..n − 1],
and a maximum knapsack of size S, compute the maximum value of the items that
we can carry, if we can either ignore or take a particular item (hence the term 0-1 for
ignore/take).
Solution
45
7.9 0 - 1 Knapsack Part 2
Problem: Given a list of weights [w1 , w2 , . . . , wn ], determine all sums that can be
constructed using the weights
Example. If the weights are [1, 3, 3, 5], the following sums are possible:
0 1 2 3 4 5 6 7 8 9 10 11 12
X X X X X X X X X X X
In this case, all sums between 0 . . . 12 are possible, except 2 and 10. For example,
the sum 7 is possible because we can select the weights [1, 3, 3].
Solution
Let possible( x, k ) = true if we can construct a sum x using the first k weights,
and otherwise possible( x, k ) = false. The recursive relation is as follows:
The formula is based on the fact that we can either use or not use the weight
wk in the sum. If we use wk , the remaining task is to form the sum x − wk
using the first k − 1 weights, and if we do not use wk , the remaining task is to
form the sum x using the first k − 1 weights. The base cases are,
true x=0
possible( x, 0) = {
false x 6= 0
46
7.10 Maximum Sum Path in a Grid
Problem: Find a path from the upper-left corner to the lower-right corner of an
n × n grid, such that we only move down and right. Each square contains a positive
integer, and the path should be constructed so that the sum of the values along the
path is as large as possible.
3 7 9 2 7
9 8 3 5 5
1 7 9 8 5
3 8 6 4 10
Example. 6 3 9 7 8
The sum of the values on the path is 67, and this is the largest possible sum on a
path from the upper-left corner to the lower-right corner.
Solution
The formula is based on the observation that a path that ends at square (y, x )
can come either from square (y, x − 1) or square (y − 1, x ):
↓
→
47
7.11 Edit Distance
Problem: Given two strings str1 and str2 and below operations that can performed
on str1. Find minimum number of edits (operations) required to convert str1 into
str2.
Example. The edit distance between LOVE and MOVIE is 2, because we can first
perform the operation LOVE → MOVE (modify) and then the operation MOVE → MOVIE
(insert). The following table shows the values of distance in the example case:
M O V I E
0 1 2 3 4 5
L 1 1 2 3 4 5
O 2 2 1 2 3 4
V 3 3 2 1 2 3
E 4 4 3 2 2 2
The lower-right corner of the table tells us that the edit distance between LOVE
and MOVIE is 2. The table also shows how to construct the shortest sequence of
editing operations. In this case the path is as follows:
M O V I E
0 1 2 3 4 5
L 1 1 2 3 4 5
O 2 2 1 2 3 4
V 3 3 2 1 2 3
E 4 4 3 2 2 2
48
Solution
return dp[m][n];
}
49
7.12 Express n as sum of k numbers
Problem: Given an integer n, how many ways can K non-negative integers less than
or equal to n add up to n?
Example. N = 5, K = 3
Output: 6
The possible combinations of integers are:
( 1, 1, 3 )
( 1, 3, 1 )
( 3, 1, 1 )
( 1, 2, 2 )
( 2, 2, 1 )
( 2, 1, 2 )
Example. N = 10, K = 4
Output: 84
Solution
This is also called as the Binomial Coefficient Problem
return C[n][k];
}
50
Part III
51
Chapter 8
Graph Theory
1 2 6
3 4 7
2 3
53
8.3 Path
A path leads from node a to node b through edges of the graph. The length of a
path is the number of edges in it. For example, the above graph contains a path
1 → 3 → 4 → 5 of length 3 from node 1 to node 5:
1 2
3 4
A path is a cycle if the first and last node is the same. For example, the above
graph contains a cycle 1 → 3 → 4 → 1. A path is simple if each node appears at
most once in the path.
8.4 Connectivity
A graph is connected when there is a path(not necessarily a direct edge) between
every pair of vertices. In a connected graph, there are no unreachable vertices. A
graph that is not connected is disconnected. For example, the following graph is
connected:
1 2
3 4
The following graph is not connected, because it is not possible to get from node
5 to any other node and vice-versa:
1 2
3 4
The connected parts of a graph are called its components. For example, the
following graph contains three components: {1, 2, 3} and {4, 5, 6, 7}
1 2 4 5
3 6 7
54
8.5 Trees
A tree is an undirected graph in which any two vertices are connected by exactly one
path. It consists of n nodes and n − 1 edges. A tree cannot contain any cycles or
self loops, however, the same does not apply to graphs. For example, the following
graph is a tree:
1 2
3 4
6 7
A binary tree is a tree in which each node has at most two children, which are
referred to as the left child and the right child. The following tree is a binary tree of
size 13 and depth 3, with 1 at the root:
2 3
4 5 6 7
8 9 10 11 12 13
1 2
4 3
55
8.6.2 Directed Graph
A directed graph is a graph in which all the edges are uni-directional i.e. the edges
point in a single direction. A typical example would be an airways map. The following
graph is directed:
1 2
4 3
1. 1 → 2 → 3 Cost = 3
2. 1 → 3 Cost = 1
3. 1 → 4 → 3 Cost = 5
1
1 2
1
3 1
4 3
2
1 2 3 4 5
56
8.7 Graph Colouring
In a coloring of a graph, each node is assigned a color so that no adjacent nodes
have the same color.
A graph is bipartite if it is possible to color it using two colors. It turns out that
a graph is bipartite exactly when it does not contain a cycle with an odd number of
edges. For example, the graph
1 2 3
4 5 6
1 2 3
4 5 6
1 2 3
4 5 6
is not bipartite, because it is not possible to color the following cycle of three nodes
using two colors:
1 2 3
4 5 6
Simplicity
A graph is simple if no edge starts and ends at the same node, and there are no
multiple edges between two nodes. Often we assume that graphs are simple. For
example, the following graph is not simple:
1 2 3
4 5 6
57
58
Chapter 9
Graph Representation
You can represent a graph in many ways. The choice of a data structure depends on
the size of the graph and the way the algorithm processes it. The two most common
ways of representing a graph is as follows:
int adj[n][n];
1 2
4 3
1 2 3 4
1 0 1 0 1
2 1 0 1 0
3 0 1 0 1
4 1 0 1 0
59
If the graph is weighted, the adjacency matrix representation can be extended
so that the matrix contains the weight of the edge if the edge exists. Using this
representation, the graph
6
1 2
7
15 10
4 3
4
1 2 3 4
1 0 6 7 15
2 6 0 10 0
3 7 10 0 4
4 15 0 4 0
The drawback of the adjacency matrix representation is that the matrix contains
n2 elements, and usually most of them are zero. For this reason, the representation
cannot be used if the graph is large as a lot of space is wasted.
vector<int> graph[n];
graph[u].push_back(v);
For a weighted graph, the weight or cost of the edge is stored along with the
vertex in the list using pairs:
60
vector<pair<int, int>> graph[n];
1 2
4 3
graph[1].push_back(2);
graph[1].push_back(4);
graph[2].push_back(1);
graph[2].push_back(3);
graph[3].push_back(2);
graph[3].push_back(4);
graph[4].push_back(3);
graph[4].push_back(1);
1 → 2 4
2 → 1 3
3 → 2 4
4 → 1 3
Another benefit of using adjacency lists is that we can efficiently find the nodes
which are directly adjacent to a given node. The following loop goes through all
nodes to which we can move from node i through an edge:
for(auto u: graph[i])
{
//we can reach u from i
}
61
62
Part IV
Graph Traversals
63
Chapter 10
Depth First Search
DFS is a simple graph traversal algorithm i.e. it is given a starting node in the graph,
and it visits all nodes that can be reached from the starting node. The DFS algorithm
is a recursive algorithm that uses the idea of backtracking. It involves exhaustive
searches of all the nodes by going ahead, if possible, else by backtracking.
In simple terms, The algorithm begins at a starting node, and proceeds to all
other nodes that are reachable from the starting node using the edges of the graph.
All the nodes will be visited on the current path till all the unvisited nodes have been
traversed after which the next path will be selected.
10.1 Implementation
This recursive nature of DFS can be implemented using recursion(duh!). The basic
idea is as follows:
The following function dfs begins a depth-first search at a given node(usually the
root node). The function assumes that the graph is stored as an adjacency list in an
array
vector<int> graph[n];
bool visited[n];
that keeps track of the visited nodes. Initially, each array value is false, and when
the search arrives at node s, the value of visited[s] becomes true. The function
can be implemented as follows:
65
void dfs(int s)
{
if (visited[s]) return;
visited[s] = true;
// process node s
for (auto u: adj[s])
{
dfs(u); //dfs on all nodes adjacent to s
}
}
10.2 Visualization
DFS on a tree is shown below. Note that the blue arrow reaches the root node at
the end.
B C
D E F G
H I J K L M
1 2
4 5
66
We may begin the search at any node of the graph; now we will begin the search at
node 1.
The search first proceeds to node 2:
1 2
4 5
1 2
4 5
The neighbors of node 5 are 2 and 3, but the search has already visited both of them,
so it is time to return to the previous nodes. Also the neighbors of nodes 3 and 2
have been visited, so we next move from node 1 to node 4:
1 2
4 5
After this, the search terminates because it has visited all nodes.
1 2
4 5
67
1 2
4 5
Since the search did not visit all the nodes, we can conclude that the graph is not
connected. In a similar way, we can also find all connected components of a graph by
iterating through the nodes and always starting a new depth-first search if the current
node does not belong to any component yet.
2. Use a stack S to keep track of the path between the start vertex and the current
vertex.
3. As soon as destination vertex z is encountered, return the path as the i.e. the
contents of the stack
68
Chapter 11
Breadth First Search
Breadth First Search (BFS) is the most commonly used approach to traverse graph.
BFS visits the nodes in increasing order of their distance from the starting node.
Thus, we can calculate the distance from the starting node to all other nodes using
breadth-first search. However, breadth-first search is more difficult to implement than
depth-first search.
Breadth-first search goes through the nodes one level after another. First the
search explores the nodes whose distance from the starting node is 1, then the nodes
whose distance is 2, and so on. This process continues until all nodes have been
visited.
11.1 Implementation
Breadth-first search is more difficult to implement than depth-first search, because
the algorithm visits nodes in different parts of the graph. A typical implementation is
based on a queue that contains nodes. At each step, the next node in the queue will
be processed.
The following code assumes that the graph is stored as adjacency lists and
maintains the following data structures:
queue<int> q;
bool visited[N];
int distance[N];
69
The search can be implemented as follows, starting at node x:
visited[x] = true;
distance[x] = 0;
q.push(x);
while (!q.empty()) {
int s = q.front(); q.pop();
// process node s
for (auto u : adj[s]) {
if (visited[u]) continue;
visited[u] = true;
distance[u] = distance[s]+1;
q.push(u);
}
}
11.2 Visualization
2 3
4 5 6 7
8 9 10 11 12 13
70
11.2.1 BFS on a normal Graph
Let us consider how breadth-first search processes the following graph:
1 2 3
4 5 6
Suppose that the search begins at node 1. First, we process all nodes that can be
reached from node 1 using a single edge:
1 2 3
4 5 6
1 2 3
4 5 6
1 2 3
4 5 6
Now we have calculated the distances from the starting node to all nodes of the
graph. The distances are as follows:
node distance
1 0
2 1
3 2
4 1
5 2
6 3
71
11.3 Application of BFS
11.3.1 Check whether Graph is Bipartite
Following is a simple algorithm to find out whether a given graph is Bipartite or not
using BFS.
1. Assign RED color to the source vertex (putting into set U).
2. Color all the neighbors with BLUE color (putting into set V).
3. Color all neighbor’s neighbor with RED color (putting into set U).
4. This way, assign color to all vertices such that it satisfies all the constraints of
m way coloring problem where m = 2.
5. While assigning colors, if we find a neighbor which is colored with same color
as current vertex, then the graph cannot be colored with 2 vertices (or graph is
not Bipartite).
11.4 0 - 1 BFS
Problem: Given a graph where every edge has weight as either 0 or k. A source
vertex is also given in the graph. Find the shortest path from source vertex to every
other vertex.
The basic idea is to take a path if it has edge 0 because that will unlock more
paths without increasing the distance. But, using a normal queue data structure , we
cannot insert and keep it sorted in O (1). Using priority queue cost us O (log n) to
keep it sorted. The problem with the normal queue is the absence of methods which
helps us to perform all of these functions :
2. Insert At the beginning (To push a vertex with same level i.e. zero weight)
3. Insert At the end (To push a vertex on next level i.e. one weight)
Fortunately, all of these operations are supported by a double ended queue (or deque
in C++ STL).
72
vector<int> d(n, INF); //distance array initialised to infinity
d[s] = 0;
deque<int> q;
q.push_front(s);
while (!q.empty()) {
int v = q.front();
q.pop_front();
for (auto edge : adj[v]) {
int u = edge.first;
int w = edge.second;
if (d[v] + w < d[u]) {
d[u] = d[v] + w;
if (w == 1)
q.push_back(u);
else
q.push_front(u);
}
}
}
73
74
Part V
75
Chapter 12
Theory
a 11 ii 14 e
4
7 6
8 10
h g f
1 2
77
8 7
b c d
4 2 9
a 11 ii 14 e
4
−8 6
8 10
h g f
1 2
On the other hand, the problem is well defined if we let edge (h, i ) have
weight −7 (no negative cycles)
• Note:
78
Chapter 13
Dijkstra’s Algorithm
Dijkstra’s algorithm for SSSP1 is a greedy algorithm for directed graphs2 with only
non-negative weights.
Dijkstra’s algorithm maintains distances to the nodes and reduces them during
the search. Dijkstra’s algorithm is efficient, because it only processes each edge in
the graph once, using the fact that there are no negative edges. It finds a shortest
path tree from a single source node, by building a set of nodes that have minimum
distance from the source
Dijkstra’s algorithm can be efficiently implemented using a priority queue in
C++ STL.
1. dist[]: an array of distances from the source node s to each node in the graph,
initialised the following way: dist[s] = 0; and for all other nodes , dist[v]
= ∞. This is done at the beginning because as the algorithm proceeds, the
dist from the source to each node v in the graph will be recalculated and
finalised when the shortest distance to v is found.
2. Q: a priority queue of all nodes in the graph. At the end of the algorithm’s
progress, Q will be empty. Q contains pairs of the form (-d, x), meaning that
the current distance to node x is d. Note that the priority queue contains
negative distances to nodes. The reason for this is that the default version
of the C++ priority queue finds maximum elements, while we want to
find minimum elements. By using negative distances, we can directly use the
default priority queue.
3. visited[]: a boolean array that indicates whether a node has been processed.
Initially all values are set to false.
1 Single Source Shortest Path : Find shortest path from source s to all vertices v ∈ V
2 Undirected Graph is a special case of directed graph with all edges having same weight
79
The steps of Dijkstra’s Algorithm to find shortest distance of all vertices from
vertex s are as follows:
1. Set all values of dist[] to ∞ except for the source vertex s, set dist[s] to 0.
Push (0,s) to Q.
2. While Q is not empty, pop the node v, that is not already set to true in
visited, from Q with the smallest dist[v]. In the first run, source node s will
be chosen because dist[s] was initialised to 0. In the next run, the next node
with the smallest dist[] value is chosen i.e. the first value in Q.
4. Update dist[] values of adjacent nodes of the current node v as follows: for
each new adjacent node u,
6. The algorithm has visited all nodes in the graph when Q is empty and found
the smallest distance to each node. dist[] now contains the shortest path tree
from source s.
13.2 Example
a) b) 4
8 7 8 7
b c d b c d
4 2 9 4 2 9
14 14
0 a 11 i 4 e 0 a 11 i 4 e
7 6 7 6
8 10 8 10
h g f h g f
1 2 1 2
8
c) 4 12 d) 4 12
8 7 8 7
b c d b c d
4 2 9 4 2 9
15
0 a 11 i 14 e a 11 i 14 e
4 4
7 6 7 6
0
8 10 8 10
h g f h g f
1 2 1 2
8 8 9
80
e) 4 12 f) 4 12 25
8 7 8 7
b c d b c d
4 2 9 4 2 9
15 15
a 11 i 14 e a 11 i 14 e
4 4
7 6 7 6
0 0
8 10 8 10
h g f h g f
1 2 1 2
8 9 11 8 9 11
g) 4 12 19 h) 4 12 19
8 7 8 7
b c d b c d
4 2 9 4 2 9
14 21 14 21
a 11 i 14 e a 11 i 14 e
4 4
7 6 7 6
0 0
8 10 8 10
h g f h g f
1 2 1 2
8 9 11 8 9 11
i) 4 12 19 j) 4 12 19
8 7 8 7
b c d b c d
4 2 9 4 2 9
14 21 14 21
a 11 i 14 e a 11 i 14 e 21
4 4
7 6 7 6
0 0
8 10 8 10
h g f h g f
1 2 1 2
8 9 11 8 9 11
Vertex in S
Vertex in V \ S
2 2 3
1 4
6 3 −5
The shortest path from node 1 to node 4 is 1 → 3 → 4 and its length is 1. However,
Dijkstra’s algorithm finds the path 1 → 2 → 4 by following the minimum weight
edges. The algorithm does not take into account that on the other path, the weight
−5 compensates the previous large weight 6.
81
13.4 Implementation
The following implementation of Dijkstra’s algorithm calculates the minimum distances
from a node x to other nodes of the graph. The graph is stored as adjacency lists so
that adj[a] contains a pair (b, w) always when there is an edge from node a to node
b with weight w.
In the following code, the priority queue q contains pairs of the form (−d, x ),
meaning that the current distance to node x is d. The array distance contains the
distance to each node, and the array processed indicates whether a node has been
processed. Initially the distance is 0 to x and ∞ to all other nodes.
Solution
82
Chapter 14
Bellman–Ford algorithm
Bellman–Ford algorithm finds shortest paths from a starting node to all nodes of a
directed graphs with any weights(but if there is a cycle with negative weight, then
this problem will be NP).
• Bellman-Ford can find negative weight cycles in graphs. In the nth step,
we can reduce any distance ⇐⇒ we have a negative cycle.
But, we don’t use Bellman-Ford every time because it is slower than Dijkstra.
The algorithm keeps track of distances from the starting node to all nodes of the
graph. Initially, the distance to the starting node is 0 and the distance to all other
nodes is ∞. The algorithm reduces the distances by finding edges that shorten the
paths until it is not possible to reduce any distance.
83
14.1 Pseudo-code
Algorithm: Bellman-Ford(int v)
Result: Single Source Shortest Path
1 d [i ] = ∞ for each vertex i;
2 d [ v ] = 0;
3 for step = 1 to n do
4 for all edges e do
5 i = e.first ; // first end of e
6 j = e.second ; // second end of e
7 w = e.weight ; // weight of edge of e
8 if d[j] greater than d[i] + w then
9 if step == n then
10 return "Negative cycle found" ; // for detecting negative cycles
11
12 end
13 d[j] = d[i] + w;
14 end
15 end
16 end
14.2 Example
Let us consider how the Bellman–Ford algorithm works in the following graph:
0 5 ∞
1 2 2
7
3 3 6
∞
3 4 2
∞ 1 ∞
Each node of the graph is assigned a distance. Initially, the distance to the starting
node is 0, and the distance to all other nodes is infinite.
The algorithm searches for edges that reduce distances. First, all edges from node
1 reduce distances:
0 5 5
1 2 2
7
3 3 5
∞
3 4 2
3 1 7
84
After this, edges 2 → 5 and 3 → 4 reduce distances:
0 5 5
1 2 2
7
3 3 5
7
3 4 2
3 1 4
0 5 5
1 2 2
7
3 3 5
6
3 4 2
3 1 4
After this, no edge can reduce any distance. This means that the distances are
final, and we have successfully calculated the shortest distances from the starting
node to all nodes of the graph.
For example, the shortest distance 3 from node 1 to node 5 corresponds to the
following path:
0 5 5
1 2 2
7
3 3 5
6
3 4 2
3 1 4
14.3 Implementation
The following implementation of the Bellman–Ford algorithm determines the shortest
distances from a node x to all nodes of the graph. The code assumes that the graph
is stored as an edge list edges that consists of tuples of the form ( a, b, w), meaning
that there is an edge from node a to node b with weight w.
The algorithm consists of n − 1 rounds, and on each round the algorithm goes
through all edges of the graph and tries to reduce the distances. The algorithm
constructs an array distance that will contain the distances from x to all nodes of
the graph. The constant INF denotes an infinite distance.
85
Solution
The time complexity of the algorithm is O (nm), because the algorithm consists
of n − 1 rounds and iterates through all m edges during a round. If there
are no negative cycles in the graph, all distances are final after n − 1 rounds,
because each shortest path can contain at most n − 1 edges.
In practice, this algorithm can be somewhat optimised: often we already get the
answer in a few phases and no useful work is done in remaining phases till n − 1, just
a waste visiting all edges. So, let’s keep the flag, to tell whether something changed
in the current phase or not, and if any phase, nothing changed, the algorithm can be
stopped.
Solution
86
14.3.1 Catching Negative Cycles
The Bellman–Ford algorithm can also be used to check if the graph contains a cycle
with negative length. For example, the graph
3 2 1
1 2 4
5 3 −7
Solution
87
14.4 SPFA Algorithm
The SPFA algorithm1 (Shortest Path Faster Algorithm) is a variant of the Bell-
man–Ford algorithm, that is often more efficient than the original algorithm. The
SPFA algorithm does not go through all the edges on each round, but instead, it
chooses the edges to be examined in a more intelligent way.
The algorithm maintains a queue of nodes that might be used for reducing the
distances. First, the algorithm adds the starting node x to the queue. Then, the
algorithm always processes the first node in the queue, and when an edge a → b
reduces a distance, node b is added to the queue.
The following implementation uses a queue q. In addition, an array inqueue
indicates if a node is already in the queue, in which case the algorithm does not add
the node to the queue again.
The worst-case running time of the algorithm is O (mn), just like the standard
Bellman-Ford algorithm. Experiments suggest that the average running time is O (m),
but this bound on the average run time has not been proved.
1 Theorigin of this algorithm is unknown. It’s said that at first Chinese coders used it in
programming contests.
88
Chapter 15
Floyd–Warshall algorithm
Floyd-Warshall algorithm solves the APSP1 problem in a directed graph with any
weights but no negative weight cycles (then it becomes NP).
1 All-Pairs Shortest Path: It calculates the value of the shortest path between each pair of nodes
in a graph.
89
forming this cycle (the total weight doesn’t matter) it should just be
negative and the number of edges is minimum.
• Often, you will notice that Floyd-Warshall technique can be applied when
we need to work with the adjacency matrix representation of a graph.
15.1 Pseudo-code
Algorithm: Floyd-Warshall
Result: All-Pairs Shortest Path
1 d [ v ][ u ] = ∞ for each pair ( v, u );
2 d [ v ][ u ] = weight( v, u ) for each adjacent pair ( v, u );
3 d [ v ][ v ] = 0 for each vertex v;
4 for i = 1 to n do
5 for j = 1 to n do
6 for k = 1 to n do
7 d[j][k] = min(d[j][k], d[j][i] + d[i][k])
8 end
9 end
10 end
15.2 Example
Let us consider how the Floyd–Warshall algorithm works in the following graph:
7
3 4 2
2 9 5
2 1 1
5
Initially, the distance from each node to itself is 0, and the distance between
nodes a and b is x if there is an edge between nodes a and b with weight x. All other
distances are infinite.
In this graph, the initial array is as follows:
1 2 3 4 5
1 0 5 ∞ 9 1
2 5 0 2 ∞ ∞
3 ∞ 2 0 7 ∞
4 9 ∞ 7 0 2
5 1 ∞ ∞ 2 0
90
The algorithm consists of consecutive rounds. On each round, the algorithm selects a
new node that can act as an intermediate node in paths from now on, and distances
are reduced using this node.
On the first round, node 1 is the new intermediate node. There is a new path
between nodes 2 and 4 with length 14, because node 1 connects them. There is also
a new path between nodes 2 and 5 with length 6.
1 2 3 4 5
1 0 5 ∞ 9 1
2 5 0 2 14 6
3 ∞ 2 0 7 ∞
4 9 14 7 0 2
5 1 6 ∞ 2 0
On the second round, node 2 is the new intermediate node. This creates new
paths between nodes 1 and 3 and between nodes 3 and 5:
1 2 3 4 5
1 0 5 7 9 1
2 5 0 2 14 6
3 7 2 0 7 8
4 9 14 7 0 2
5 1 6 8 2 0
On the third round, node 3 is the new intermediate round. There is a new path
between nodes 2 and 4:
1 2 3 4 5
1 0 5 7 9 1
2 5 0 2 9 6
3 7 2 0 7 8
4 9 9 7 0 2
5 1 6 8 2 0
The algorithm continues like this, until all nodes have been appointed intermediate
nodes. After the algorithm has finished, the array contains the minimum distances
between any two nodes:
1 2 3 4 5
1 0 5 7 3 1
2 5 0 2 8 6
3 7 2 0 7 8
4 3 8 7 0 2
5 1 6 8 2 0
For example, the array tells us that the shortest distance between nodes 2 and 4
is 8. This corresponds to the following path:
91
7
3 4 2
2 9 5
2 1 1
5
15.3 Implementation
The time complexity of the algorithm is O (n3 ), because it contains three nested
loops that go through the nodes of the graph.
Since the implementation of the Floyd–Warshall algorithm is simple, the algorithm
can be a good choice even if it is only needed to find a single shortest path in the
graph. However, the algorithm can only be used when the graph is so small that a
cubic time complexity is fast enough.
92
15.3.1 Detecting Negative Cycles
Run Floyd-Warshall algorithm on the graph. Initially d[v][v] = 0 for each v. But
after running the algorithm d[v][v] will be smaller than 0 if there exists a negative
length path from v to v. We can use this to find all pairs of vertices that don’t have
a shortest path between them.
93
94
Part VI
Mathematics
95
Chapter 16
Basic Maths
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
int main()
{
cpp_int x; //Can have arbitrary precision
}
97
For example, the following code calculates n!, the factorial of n, modulo m:
long long x = 1;
for (int i = 2; i <= n; i++) {
x = (x*i)%m;
}
cout << x%m << "\n";
float pi = 3.14159;
cout << fixed << setprecision(3) << pi; //3.142
16.5 Mathematics
16.5.1 Sum formulas
n
n ( n + 1)
∑ x = 1 + 2 + 3 + ... + n = 2
x =1
and
n
n(n + 1)(2n + 1)
∑ x 2 = 12 + 22 + 32 + . . . + n 2 = 6
.
x =1
and
n
n2 ( n + 1)2
∑ x3 = 13 + 23 + 33 + . . . + n3 =
4
x =1
where,
a is the first number,
b is the last number and
n is the amount of numbers.
98
16.5.3 Sum of Geometric Progression
bk − a
a + ak + ak2 + · · · + b =
k−1
where,
a is the first number,
b is the last number and
the ratio between consecutive numbers is k.
A special case of a sum of a geometric progression is the formula
1 + 2 + 4 + 8 + . . . + 2n−1 = 2n − 1.
99
100
Chapter 17
Number Theory
Solution
We do the following optimisation:
√
1. Instead of checking till n, we can check till n
2. The algorithm can be improved further by observing that all primes are
of the form 6k ± 1, with the exception of 2 and 3.
bool isPrime(int n)
{
if (n <= 1) return false;
if (n <= 3) return true;
if (n%2 == 0 || n%3 == 0) return false;
for (int i=5; i*i<=n; i=i+6)
if (n%i == 0 || n%(i+2) == 0)
return false;
return true;
}
101
17.2 Sieve of Eratosthenes
The sieve of Eratosthenes is one of the most efficient ways to find all primes smaller
than n when n is smaller than 10 million
Problem: Given a number n, print all primes smaller than or equal to n. It is also
given that n is a small number.
Solution
Following is the algorithm to find all the prime numbers less than or equal to a
given integer n by Eratosthenes’s method:
2. The algorithm iterates through the numbers 2...n one by one. Always
when a new prime x is found, the algorithm records that the multiples of
x (2x, 3x, 4x, ...) which are ≥ x2 are not primes, because the number x
divides them.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
0 0 0 0 2 0 2 0 2 3 2 0 3 0 2 3 2 0 3 0 2
Time Complexity is O (n log (log n)) which is very close to linear O (n)
102
17.3 Prime Factorization
Problem: Given a number n, print all prime factors of n.
Solution
Following are the steps to find all prime factors.
vector<int> factors(int n)
{
vector<int> f;
while (n%2 == 0)
{
f.push_back(2);
n = n/2;
}
for (int i = 3; i <= sqrt(n); i = i+2)
{
while (n%i == 0)
{
f.push_back(i);
n = n/i;
}
}
// This condition is to handle the case when n
// is a prime number greater than 2
if (n > 2)
f.push_back(n);
return f;
}
√
Time Complexity is O ( n)
There is an even efficient solution which uses Sieve of Eratosthenes to pre
compute prime numbers. It has time complexity O (log n). You can read about
it on GeeksforGeeks or CP3 book.
103
for very large numbers. In competitive programming, some problems are based on
these common conjectures.
Conjecture 1 (Goldbach’s conjecture): Each even integer n >2 can be represented
as a sum n = a + b so that both a and b are primes.
Conjecture 2 (Goldbach’s weak conjecture): Every odd number greater than 5 can
be expressed as the sum of three primes. (A prime may be used more than once in
the same sum.) This is trivial to prove if the above conjecture is proved to be true.
Conjecture 3 (Twin prime conjecture): There is an infinite number of pairs of the
form p, p + 2, where both p and p + 2 are primes.
Conjecture 4 (Legendre’s conjecture): There is always a prime between numbers
n2 and (n + 1)2 , where n is any positive integer.
Conjecture 5 (Collatz Conjecture): Collatz conjecture states that a number n
converges to 1 on repeatedly performing the following operations:
n → n/2 if n is even
n → 3n + 1 if n is odd
This has been verified for numbers up to 5.6 × 1013 . For example is x = 3, then:
3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
Conjecture 6 (Mersenne Prime Conjecture): There are infinitely positive integers n
for which 2n − 1 is a prime number. (There are currently 47 Mersenne primes known)
104
17.5 Modulo of Big Number
Problem: Given a big number num represented as string and an integer x, find value
of num mod x. Output is expected as an integer.
Solution
The idea is to process all digits one by one and use the property that ( xy)
mod a ≡ ( x mod a × y mod a) mod a. Below is the implementation.
Solution
The naive solution would run in O (n) time. Using modular exponentiation we
can bring down the complexity to O (log n) by using the following algorithm:
1 n=0
n
x = { x n/2 · x n/2 n is even
x n −1 · x n is odd
105
17.7 Euler’s Totient Function
Euler’s totient function ϕ(n) gives the number of coprime numbers to n between
1 and n. For example, ϕ(14) = 6, because 1, 3, 5, 9, 11 and 13 are coprime to 14.
The value of ϕ(n) can be calculated from the prime factorization of n using the
formula
1
ϕ(n) = n ∏ 1 −
p|n
p
Where the product is over the distinct prime numbers dividing n.Note that
ϕ(n) = n − 1 if n is prime. The implementation of totient function is shown below.
int totient(int n)
{
int result = n;
for (int p = 2; p * p <= n; ++p)
{
if (n % p == 0)
{
while (n % p == 0) n /= p;
result -= result / p;
}
}
106
17.8.2 Wilson’s Theorem
Wilson’s theorem states that a number n is prime exactly when
(n − 1)! ≡ −1 (mod n)
OR
( n − 1) ! (mod n) = n − 1
However the theorem cannot be applied to large values of n, because it is difficult to
calculate values of (n − 1)! when n is even as large as 50.
F0 = 0, F1 = 1, Fn = Fn−1 + Fn−2
The first elements of the sequence are:
Cassini’s identity
Addition Rule
Fk Fn+1 + Fk−1 Fn
Fn+k = {
F2n = Fn ( Fn+1 + Fn−1 ), if k = n
GCD identity
GCD ( Fm , Fn ) = FGCD(m,n)
Zeckendorf’s Theorem
Zeckendorf’s theorem states that every positive integer can be represented uniquely
as the sum of one or more distinct Fibonacci numbers in such a way that the sum
does not include any two consecutive Fibonacci numbers. More precisely, if N is any
positive integer, there exist positive integers ci ≥ 2, with ci+1 > ci + 1, such that
k
N = ∑ Fci
i =0
107
17.8.4 Pythagorean Triples
A Pythagorean triple is a triple ( a, b, c) that satisfies the Pythagorean theorem
a2 + b2 = c2 , which means that there is a right triangle with side lengths a, b and c.
For example, (3, 4, 5) is a Pythagorean triple.
If ( a, b, c) is a Pythagorean triple, all triples of the form (ka, kb, kc) are also
Pythagorean triples where k>1. A Pythagorean triple is primitive if a, b and c are
coprime, and all Pythagorean triples can be constructed from primitive triples using a
multiplier k.
Euclid’s formula can be used to produce all primitive Pythagorean triples. Each
such triple is of the form
(n2 − m2 , 2nm, n2 + m2 ),
where 0<m<n, n and m are coprime and at least one of n and m is even. For
example, when m = 1 and n = 2, the formula produces the smallest Pythagorean
triple
(22 − 12 , 2 · 2 · 1, 22 + 12 ) = (3, 4, 5).
For linear Diophantine equation equations, integral solutions exist if and only if,
the GCD of coefficients of the two variables divides the constant term perfectly.
In other words the integral solution exists if, gcd( a, b)|c.
Thus the algorithm to determine if an equation has integral solution is pretty
straightforward.
108
17.10 Euclid’s Algorithm for GCD
Problem: Given two non-negative integers a and b, we have to find their gcd(greatest
common divisor), i.e. the largest number which is a divisor of both a and b. It’s
commonly denoted by gcd( a, b). Mathematically it is defined as:
gcd( a, b) = max k.
k=1...∞ : k| a ∧k |b
(here the symbol "|" denotes divisibility, i.e. "k | a" means "k divides a")
Solution
The algorithm is extremely simple:
a, if b = 0
gcd( a, b) = {
gcd(b, a (mod b)), otherwise.
109
17.11 Modular Inverse
The inverse of x (mod m) is a number x −1 such that
xx −1 (mod m) ≡ 1
Using modular inverses, we can divide numbers modulo m, because division by x
corresponds to multiplication by x −1 . For example, to evaluate the value of 36/6
(mod 17), we can use the formula 2 × 3 (mod 17), because 36 (mod 17) = 2 and
6−1 (mod 17) = 3.
However, a modular inverse does not always exist. For example, if x = 2 and
m = 4, the equation
xx −1 (mod m) = 1
cannot be solved, because all multiples of 2 are even and the remainder can never be
1 when m = 4. It turns out that the value of x −1 mod m can be calculated exactly
when x and m are coprime.
A short one-liner to compute modular inverse when x and m are coprime is shown
below
110
17.12 Chinese Remainder Theorem
Problem: Find x that satisfies the following equations:
x ≡ a1 (mod m1 )
x ≡ a2 (mod m2 )
x ≡ a3 (mod m3 )
···
x ≡ an (mod mn )
where all pairs of m1 , m2 , . . . , mn are coprime.
Solution
Let M = m1 × m2 × m3 × · · · × mn
Let M1 , M2 , M3 , · · · , Mn be such that
M1 = M/m1
M2 = M/m2
M3 = M/m3
···
Mn = M/mn
Let y1 , y2 , y3 , · · · , yn be such that yi is the modular inverse of Mi i.e.,
M1 × y1 ≡ 1 (mod m1 )
M2 × y2 ≡ 1 (mod m2 )
M3 × y3 ≡ 1 (mod m3 )
···
Mn × yn ≡ 1 (mod mn )
Then x ≡ a1 M1 y1 + a2 M2 y2 + a2 M2 y2 + · · · + an Mn yn (mod M )
x + kM
Example.
x ≡ 3 (mod 8)
x ≡ 1 (mod 9)
x ≡ 4 (mod 11)
∴ M = 8 × 9 × 11 = 792
M1 = 792/8 = 99
M2 = 792/9 = 88
M3 = 792/11 = 72
111
99 × y1 ≡ 1 (mod 8) or y1 = 3
88 × y2 ≡ 1 (mod 9) or y2 = 4
72 × y3 ≡ 1 (mod 11) or y3 = 2
∴ x = 3 × 99 × 3 + 1 × 88 × 3 + 4 × 72 × 2 = 1819
∴ x can be 1819, 2611, 3403 · · ·
112
Part VII
Question Bank
113
Chapter 18
ZCO Questions
Zonal Computing Olympiad is the first stage of the Indian Computing Olympiad.
The Indian Computing Olympiad is used to select the team of four students to
represent India at the International Olympiad for Informatics (IOI). IOI is one of
the twelve international Science Olympiads held annually. Other prominent Science
Olympiads include Mathematics, Physics, Chemistry, Biology and Astronomy.
The problems are quite easy and mostly related to these topics:
• Basic Maths
• Dynamic Programming
• Greedy Algorithms
• Sorting
You can see and submit solutions of the problems on Codechef: https://www.
codechef.com/ZCOPRAC.
The question bank starts from the next page.
115
18.1 Smart Phone
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO14003
Problem Code: ZCO14003
Year: ZCO 2014
Problem: You are developing a smartphone app. You have a list of potential
customers for your app. Each customer has a budget and will buy the app at your
declared price if and only if the price is less than or equal to the customer’s budget.
You want to fix a price so that the revenue you earn from the app is maximised.
Find this maximum possible revenue.
For instance, suppose you have 4 potential customers and their budgets are 30,
20, 53 and 14. In this case, the maximum revenue you can get is 60.
Input Format: Line 1: N, the total number of potential customers.
Lines 2 to N + 1: Each line has the budget of a potential customer.
Output Format: The output consists of a single integer, the maximum possible
revenue you can earn from selling your app.
Example
4
30
20
53
14
60
Example
5
40
3
65
33
21
99
Constraints:
116
Note: The answer might not fit in a variable of type int. We recommend that
you use variables of type long long to read the input and compute the answer. If
you use printf and scanf, you can use %lld for long long.
Solution
The key observation in this problem is that our optimal budget will be among
the customer’s budget. In the first example our optimal budget can be 30 or
20.
Naive Solution: Simply try all combinations by putting price equal to every
element one by one and check the number of integers/elements bigger than
this price. Multiplying price and the bigger numbers will give the revenue for
that price. Take the maximum of all such revenues and output it. Since we
are trying every element and checking the bigger numbers in whole array takes
O ( N ), total time complexity comes out to be O ( N 2 ).
Correct solution: First sort the array. Then take the maximum of a[i ] ∗ ( N −
i ) for all i in the range [0, N ). This works because all elements after the ith
element(including the ith element) are greater than or equal to a[i ] and there
are N − i elements after i including i itself.
Code: https://repl.it/@SalilGokhale/Smart-Phone
117
18.2 Video Game
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO14001
Problem Code: ZCO14001
Year: ZCO 2014
Problem: You are playing a video game in which several stacks of boxes are lined
up on the floor, with a crane on top to rearrange the boxes, as shown in the picture
below.
• Move one position left (does nothing if already at the leftmost position)
• Move one position right (does nothing if already at the rightmost position)
• Pick up a box from the current stack (does nothing if the crane already has a
box)
• Drop a box on the current stack (does nothing if the crane doesn’t already
have a box)
1. Pick up box
2. Move right
3. Move right
4. Move right
118
5. Move right
6. Drop box
7. Move left
8. Pick up box
9. Move left
the number of boxes in each stack from left to right would be 2,1,3,1,4,0,1.
Input Format: Line 1 : The width of the game (the number of stacks of boxes), N,
followed by the max height H of each stack.
Line 2 : N integers, the initial number of boxes in each stack, from left to right.
Each number is ≤ H.
Line 3 : A sequence of integers, each encoding a command to the crane. The
commands are encoded as follows
• 1 → Move left
• 2 → Move right
• 3 → Pick up box
• 4 → Drop box
• 0 → Quit
The command Quit(0) appears exactly once, and is the last command.
The initial position of the crane is above the leftmost stack, with the crane not
holding any box.
Output Format: A single line with N integers, the number of boxes in each stack,
from left to right.
Example
7 4
3 1 2 1 4 0 1
3 2 2 2 2 4 1 3 1 4 0
2 1 3 1 4 0 1
119
Example
3 5
2 5 2
3 2 4 2 2 2 1 4 1 1 1 1 0
1 5 2
Constraints:
Solution
The problem statement is long and intimidating but in reality this is a simple
implementation problem. There are no techniques or optimisation. You have
to do as directed!!! You can use an array or a vector to store the number of
boxes in each stack after every operation.
Code: https://repl.it/@SalilGokhale/Video-Game
120
18.3 Tournament
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO13001
Problem Code: ZCO13001
Year: ZCO 2013
Problem: N teams participate in a league cricket tournament on Mars, where each
pair of distinct teams plays each other exactly once. Thus, there are a total of
N ×( N −1)
2 matches. An expert has assigned a strength to each team, a positive
integer. Strangely, the Martian crowds love one-sided matches and the advertising
revenue earned from a match is the absolute value of the difference between the
strengths of the two matches. Given the strengths of the N teams, find the total
advertising revenue earned from all the matches.
For example, suppose N is 4 and the team strengths for teams 1, 2, 3, and 4 are
3, 10, 3, and 5 respectively. Then the advertising revenues from the 6 matches are as
follows:
4
3 10 3 5
23
Constraints:
In all subtasks, the strength of each team is an integer between 1 and 103 inclusive
2 ≤ N ≤ 2 × 105
Note: The answer might not fit in a variable of type int. We recommend that
type long long be used for computing all advertising revenues. If you use printf
and scanf, you can use %lld for long long.
121
Solution
The first impulse would be to calculate all the match revenues and then sum
up. but it would be O (n2 ) which will surely give TLE.
The key to the problem lies in an observation. Let’s consider an example and
then generalise it.
Let us take a different example than the given sample case.
4
3 10 7 5
but wait that’s not what we will do. Look at it in the following manner:
10 × (3 − 0) + 7 × (2 − 1) + 5 × (1 − 2) + 3 × (0 − 3)
But make sure to sort the array first. Time Complexity is O (n log n)
Code: https://repl.it/@SalilGokhale/Tournament
122
18.4 Variation
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO15002
Problem Code: ZCO15002
Year: ZCO 2015
(i, j) : 1 ≤ i ≤ j ≤ Nand | ai − a j |≥ Kj
For example if K = 1 and the sequence is 3,2,4 the answer is 3. If K = 1 and the
sequence is 3, 1, 3 then the answer is 2.
Your task is to write a program that takes a sequence and the value K as input
and computes the total variation count.
Input Format: The first line contains two positive integers N and K, separated by
a space.
This is followed by a line containing N integers separated by space giving the
values of the sequence.
Output Format: A single integer in a single line giving the total variation count.
Example
3 1
3 1 3
Constraints:
You may assume that all integers in the input are in the range 0 to 108 inclusive.
1 ≤ N ≤ 65000, 1 ≤ K ≤ 108
123
Solution
The first thing to note is that we have to take the absolute value of the
difference of the two elements. This simply means that the order in which we
discover the elements, ( ai , a j ) satisfying the condition, ( ai − a j ≥ k) doesn’t
matter. Also, the constraints are small enough to allow sorting. So, we just
sort the array.
Now that the array is sorted, we can naively check for the left most element
a1 , how many a j satisfy the given condition. This can be done in O(n2 ). But
this is not good enough to pass the second sub-task.
Taking it a step further - as the elements are sorted - we can see that a sliding
window would do the job in O (n) time.
In this, we’ll iterate over the array maintaining two indices, a and b starting
from the leftmost part of the array. The basic aim is to find the first a[ j] for a
particular i, such that it’s the smallest element, where a[ j] − a[i ] ≥ k. This can
be done by incrementing i, if a[ j] − a[i ] ≥ k, else increment j. For a particular
i, the number of matching pairs will be N − j. The sum of matching elements
for all i is the answer.
Code: https://repl.it/@SalilGokhale/Variation
124
18.5 Matched Brackets
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO12001
Problem Code: ZCO12001
Year: ZCO 2012
Problem: A sequence of opening and closing brackets is well-bracketed if we can
pair up each opening bracket with a matching closing bracket in the usual sense. For
instance, the sequences (), (()) and ()(()) are well-bracketed, while (, ()), (()(), and
)( are not well-bracketed.
The nesting depth of a well-bracketed sequence tells us the maximum number of
levels of inner matched brackets enclosed within outer matched brackets. For instance,
the nesting depth of () and ()()() is 1, the nesting depth of (()) and ()(()) is 2, the
nesting depth of ((())) is 3, and so on.
Given a well-bracketed sequence, we are interested in computing the following:
• The nesting depth, and the first position where it occurs-this will be the
position of the first opening bracket at this nesting depth, where the positions
are numbered starting with 1.
For instance, the nesting depth of ()(())()(()())(()()) is 2 and the first position
where this occurs is 4. The opening bracket at position 10 is also at nesting depth 2
but we have to report the first position where this occurs, which is 4.
In this sequence, the maximum number of symbols between a pair of matched
bracket is 6, starting at position 9. There is another such sequence of length 6 starting
at position 15, but this is not the first such position.
Input Format: The input consists of two lines. The first line is a single integer
N, the length of the bracket sequence. Positions in the sequence are numbered
1, 2, · · · , N. The second line is a sequence of N space-separated integers that encode
the bracket expression as follows: 1 denotes an opening bracket ( and 2 denotes a
closing bracket ). Nothing other than 1 or 2 appears in the second line of input and
the corresponding expression is guaranteed to be well-bracketed.
Output Format: Your program should print 4 space-separated integers in a line,
denoting the four quantities asked for in the following order: nesting depth, first
position that achieves the nesting depth, length of the maximum sequence between
matching brackets and the first position where such a maximum length sequence
occurs
125
Example
20
1 2 1 1 2 2 1 2 1 1 2 1 2 2 1 1 2 1 2 2
2 4 6 9
Constraints:
You may assume that 2 ≤ N ≤ 105 . In 30% of the test cases, 2 ≤ N ≤ 103 .
Solution
Maximum depth: To find the maximum depth, you can maintain variables
depth and maximum_depth. Increment it if character is 1 and decrement it if
character is 2. Each time you increment it, you can check if depth exceeds
maximum_depth and update maximum_depth to depth. You must store the index
when you update maximum_depth.
126
18.6 Chewing
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO13003
Problem Code: ZCO13003
Year: ZCO 2013
Problem: Hobbes has challenged Calvin to display his chewing skills and chew two
different types of Chewing Magazine’s Diabolic Jawlockers chewing gum at the same
time. Being a generous sort of tiger, Hobbes allows Calvin to pick the two types of
gum he will chew.
Each type of chewing gum has a hardness quotient, given by a non-negative
integer. If Calvin chews two pieces of gum at the same time, the total hardness
quotient is the sum of the individual hardness quotients of the two pieces of gum.
Calvin knows that he cannot chew any gum combination whose hardness quotient
is K or more. He is given a list with the hardness quotient of each type of gum in the
Diabolic Jawlockers collection. How many different pairs of chewing gum can Calvin
choose from so that the total hardness quotient remains strictly below his hardness
limit K?
For instance, suppose there are 7 types of chewing gum as follows:
If Calvin’s hardness limit is 4, there are 4 possible pairs he can choose: type 2
and 7 (1 + 0 < 4), type 3 and 7 (3 + 0 < 4), type 2 and 4 (1 + 1 < 4) and type 4
and 7 (1 + 0 < 4).
Input Format: Line 1 : Two space separated integers N and K, where N is the
number of different types of chewing gum and K is Calvin’s hardness limit.
Line 2: N space separated non-negative integers, which are the hardness quotients
of each of the N types of chewing gum.
Output Format: The output consists of a single non-negative integer, the number
of pairs of chewing gum with total hardness quotient strictly less than K.
Example
7 4
10 1 3 1 5 5 0
Constraints:
In all subtasks, you may assume that all the hardness quotients as well as the
hardness limit K are between 0 and 106 inclusive.
2 ≤ N ≤ 105
127
Note: The answer might not fit in a variable of type int. We recommend that
type long long be used for computing the answer. If you use printf and scanf, you
can use %lld for long long.
Solution
Naive Solution: Checking all pairs and checking if their sum is below k gives
a time complexity of O (n2 ) and TLE.
This problem can be solved using the two pointer-technique. We first sort the
array. Then we apply the two-pointer technique. Whenever we get two pointer
i, j such that hardness[i] + hardness[j < k, we add j − i to a variable sum
because all such i and j can be paired up. We stop when i == j
Code: https://repl.it/@SalilGokhale/Chewing
128
18.7 SUPW
URL: https://www.codechef.com/ZCOPRAC/problems/ZCO14002
Problem Code: ZCO14002
Year: ZCO 2014
Problem: In ICO School, all students have to participate regularly in SUPW. There
is a different SUPW activity each day, and each activity has its own duration. The
SUPW schedule for the next term has been announced, including information about
the number of minutes taken by each activity.
Nikhil has been designated SUPW coordinator. His task is to assign SUPW duties
to students, including himself. The school’s rules say that no student can go three
days in a row without any SUPW duty.
Nikhil wants to find an assignment of SUPW duty for himself that minimizes the
number of minutes he spends overall on SUPW.
Input Format: Line 1: A single integer N, the number of days in the future for
which SUPW data is available.
Line 2: N non-negative integers, where the integer in position i represents the
number of minutes required for SUPW work on day i.
Output Format: The output consists of a single non-negative integer, the minimum
number of minutes that Nikhil needs to spend on SUPW duties this term
Example
10
3 2 1 1 2 3 1 3 2 1
Example
8
3 2 3 2 3 5 1 3
Constraints:
1 ≤ N ≤ 2 × 105
The number of minutes of SUPW each day is between 0 and 104 , inclusive
Solution
Code: https://repl.it/@SalilGokhale/Smart-Phone
129