Introduction & Basics
UMD CP Club Summer CP
Who am I?
Cheng-Yuan (Sam) Lee
.Rising Sophomore
.ICPC Mid-Atlantic 3rd Place
.UMD CP Club President
.Codeforces Master
.AtCoder Rating \(\ge\) 2000
Feel free to discuss problems with me!
What is Competitive Programming
The problems are usually algorithmic
(CMSC351/451)
You have to come up with an algorithm
and implement It!
There are also two important parts
Time and Space
Time and Space
If Your Code didn't fit in Time
If Your Code didn't fit in Time
You get TLE
(Time Limit Exceeded)
If Your Code didn't fit in Space
You get MLE
(Memory Limit Exceeded)
If Your Code is incorrect
You get WA
(Wrong Answer)
When you finally solved a problem, you get AC !
(Accepted)
Ratings!
T-Shirt!
Resources
Time Complexity
In C++
In C++
It varies from \(10^8 \sim 5 \times 10^8\)
We need a tool to approximate how many operations our code does!
Big O Notation
Big O Notation
We denote the runtime of an algorithm with
where \(n\) is the input size
\(f(n)\) is the number of operations with respect to \(n\)
Big O Notation
The formal definition of Big O is
if and only if
Big O Notation
The formal definition of Big O is
if and only if
We ignore the constant factor
Big O Notation
The other viewpoint of Big O is
if and only if
Big O Notation
In CP, we usually plug \(n\) in
and divide by \(10^8 \sim 5 \times 10^8\)
1. Counting Loops
for (int i = 1; i <= n; i++) {
sum += i;
}
What is the time complexity of this code?
1. Counting Loops
for (int i = 1; i <= 2*n; i++) {
sum += i;
}
What is the time complexity of this code?
1. Counting Loops
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
sum += i * j;
}
}
What is the time complexity of this code?
1. Counting Loops
for (int i = 2; i <= n; i++) {
for(int j = 2 * i; j <= n; j += i){
isprime[j] = false;
}
}
What is the time complexity of this code?
1. Counting Loops
for (int i = 2; i <= n; i++) {
for(int j = 2 * i; j <= n; j += i){
isprime[j] = false;
}
}
What is the time complexity of this code?
1. Counting Loops
2. Recursions
int f(int x){
if (x <= 1) {
return 1;
} else {
return f(x-1) + f(x-2);
}
}
What is the time complexity of this code?
2. Recursions
2. Recursions
You will learn different ways to approximate time complexity of recursion in CMSC351
Recursion Tree
Substitution Method
Master's Theorem
Knowing Master's Theorem will be sufficient
Big O Notation
Programming Language
Which Language Should I Use?
In IOI (International Olympiad in Informatics)
The only allowed language is C/C++
In ICPC
The allowed languages are
C/C++, Java, Python, Kotlin
Most Competitive Programmer Uses C++
We will be using C++
Basic C++ Syntax
#include <bits/stdc++.h>
using namespace std;
int main(){
int a, b;
cin >> a >> b;
cout << a+b << "\n";
}
A+B Code
#include <bits/stdc++.h>
This header includes all the common headers
cin >>
cout <<
The above input things from standard input
The bottom output things to standard output
for (int i = 0; i < n; i++) {
//do something n times
}
while(condition) {
//do something when condition is true
}
Loops
The range of int
is approxiamtely
\([-2 \times 10^9, 2 \times 10^9]\)
Important!
The range of long long
is
\([-9 \times 10^{18}, 9 \times 10^{18}]\) |
There was a debate between
cin, cout
and scanf(), printf()
cin, cout
are slow!
Try this problem
If you use scanf, printf
You get AC
If you use cin, cout
You get TLE
Does that mean you shouldn't use cin, cout?
NO!
ios_base::sync_with_stdio(0); cin.tie(0);
Add these two lines to your code!
cin, cout is now 6x faster than scanf, printf !
Another Important Thing
Another Important Thing
Use "\n" instead of endl
First one uses cin, cout and endl
Second one uses cin, cout and "\n"
C++ STL & Basic Data Structures
Immutable Arrays
int arr[n];
long long arr[n];
char arr[n];
string arr[n];
An array is initialized with T arr[length]
where T
is some type and length
is some integer
The size cannot be changed
Immutable Arrays
int arr[n] = {1, 2, 3};
cout << arr[0] << "\n"; // 1
cout << arr[1] << "\n"; // 2
arr[1] = 0;
cout << arr[1] << "\n"; // 0
You can access elements with arr[i]
it is 0-based, the first element is arr[0]
Dynamic Arrays
There are two types of dynamic arrays
vector<T>
deque<T>
vector supports pushing back but not front
deque supports both pushing back and front
Dynamic Arrays - vector<T>
vector<int> v;
v.push_back(1); // v = {1}
cout << v[0] << "\n"; // 1
v.push_back(2); // v = {1, 2}
v.pop_back(); // v = {1}
v.push_back(3); // v = {1, 3}
cout << v[1] << "\n"; // 3
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
Dynamic Arrays - vector<T>
vector<int> v(len, x);
//initialize a vector of length len and initial element x
v[i]; //access the ith element
v.push_back(x); //add x to back
v.pop_back(); //remove last element
v.size(); //returns size of the vector
v.empty(); //returns true or false
v.resize(len); //resize the vector to len
v.clear(); //clear the vector
Dynamic Arrays - string
string in C++ is technically just vector<char>
but it also has
string s = "Hello";
s += "World";
cin >> s;
cout << s;
Dynamic Arrays - deque<T>
deque<int> dq;
dq.push_back(1); // v = {1}
cout << dq[0] << "\n"; // 1
dq.push_back(2); // v = {1, 2}
dq.pop_front(); // v = {2}
dq.push_back(3); // v = {2, 3}
cout << dq[0] << "\n"; // 2
for (int i = 0; i < dq.size(); i++) {
cout << dq[i] << " ";
}
Dynamic Arrays - deque<T>
deque<int> dq(len, x);
//initialize a deque of length len and initial element x
dq[i]; //access the ith element
dq.push_back(x); dq.push_front(x);//add x to back/front
dq.pop_back(); dq.pop_front(); //remove last/first element
dq.size(); //returns size of the deque
dq.empty(); //returns true or false
dq.resize(len); //resize the deque to len
dq.clear(); //clear the deque
You might ask, why don't we always use deque?
It has a larger constant!
How to sort an array?
In C++, there are builtin functions that are fast and easy to use!
How to sort an array?
int arr[n] = {5, 4, 3, 2, 1};
vector<int> v = {3, 4, 5, 1, 2};
sort(arr, arr+n);
sort(v.begin(), v.end());
std::sort
has a time complexity of \(O(n \log n)\)
Other useful methods (requires sort)
lower_bound(v.begin(), v.end(), x);
//return the iterator to first >= x
upper_bound(v.begin(), v.end(), x);
//return the iterator to first > x
unique(v.begin(), v.end());
//if v has k unique elements, set v[0], v[1], v[k-1] to those elements
Both lower and upper bound takes \(O(\log n)\)
unique takes \(O(n)\)
Other useful methods
swap(&a, &b); //swap the value of a and b
fill(v.begin(), v.end(), x) //make all elements in range x
reverse(v.begin(), v.end()); //reverse the range
max_element(v.begin(), v.end()); //return iterator to maximum element
min_element(v.begin(), v.end()); //return iterator to minimum element
shuffle(v.begin(), v.end()); //random shuffle all elements in range
swap is \(O(1)\)
others are \(O(n)\)
Pairs
Consider this simple task
You are given \(n\) points on 2d plane,
sort them first by \(x\) then by \(y\)
Pairs
pair<T, T> p;
p.first; //returns first element
p.second; //returns second element
pair<T,T> make_pair(T a, T b); //return a pair {a,b}
{a, b}; //same as above
Pairs
vector<pair<int,int>> v;
for (int i = 0; i < n; i++) {
v.push_back({x[i], y[i]});
}
sort(v.begin(), v.end());
Stack
It is a Last in First Out Data Structure
stack<int> st;
st.push(x); //push back
st.pop(); //pop back
st.top(); //returns last element
st.empty(); //return true or false
st.size(); //return size of stack
This is monotonic stack, we will focus on it more later
Queue
It is a First in First Out Data Structure
queue<int> q;
q.push(x); //push front
q.pop(); //pop front
q.front(); //returns first element
q.empty(); //return true or false
q.size(); //return size of queue
Queue
This data structure is used for BFS
We will use it more when we talk about it
Priority Queue (Heap)
You can simply used it as a sorted queue
priority_queue<int> pq;
pq.push(x); //push x into the queue
pq.pop(); //pop the largest element
pq.top(); //returns the largest element
pq.empty(); //return true or false
pq.size(); //return size of queue
push and pop are \(O(\log n)\)
Priority Queue (Heap)
You can also create min heap (default is max heap)
priority_queue<int, vector<int>, greater<>> pq;
pq.push(x); //push x into the queue
pq.pop(); //pop the smallest element
pq.top(); //returns the smallest element
pq.empty(); //return true or false
pq.size(); //return size of queue
This data structure is used the most in Greedy algorithms
Binary Search Tree
There are two builtin binary search trees
set<T>
map<K,V>
Both are ordered
set
set<int> st;
st.insert(x); //add x to set
st.erase(x); //remove x from set
st.count(x); //most used as finding x
st.find(x); //it is harder to use than count
st.empty(); //return true or false
st.size(); //return size
st.lower_bound(x); //return iterator to first >= x
st.upper_bound(x); //return iterator to first > x
multiset
multiset<int> st;
st.insert(x); //add x to set, O(log n)
st.erase(x); //remove all x from set, O(log n)
st.count(x); //count the number of x, O(log n)
st.find(x); //find the first x, O(log n)
st.empty(); //return true or false, O(1)
st.size(); //return size, O(1)
st.lower_bound(x); //return iterator to first >= x, O(log n)
st.upper_bound(x); //return iterator to first > x, O(log n)
multiset
If you only want to remove an element
Use st.erase(st.find(x));
map
You can use it as an array for any data type
map<string, int> mp;
mp["hello"] = 1; //O(log n)
mp.insert("a", 2); // O(log n)
cout << mp["a"] << "\n"; // 2
mp.erase("hello"); // O(log n)
//other things are the same as set
mp.erase(Key); //remove Key from map O(log n)
mp.count(Key); //check if Key exists in map O(log n)
mp.find(Key); // O(log n)
mp.lower_bound(key); // O(log n)
mp.upper_bound(key); // O(log n)
unordered_map (hash table)
Same as map, but doesn't support set operations
Everything becomes \(O(1)\) (expected)
These are just the basics, but it is important to know them
We will start discussing actual algorithms in the following weeks
UMD Summer CP - Introduction & Basics
By sam571128
UMD Summer CP - Introduction & Basics
- 302