NATIONAL COMPETITION PREPARATION · ELEMENTARY SCHOOL
Preparation for the National Competition in Informatics
This page is a guide for solving tasks that appear at the national (state) competitions in informatics
for elementary schools in Serbia. Along with the tasks, there are simple preparatory examples that are
building blocks of larger tasks. Every task has detailed hints and a C++ solution (and where a more
efficient solution exists – several of them), which you can reveal after you try to solve the task
yourself. There is a quiz at the end of the page.
You can also download the tasks with solutions and test cases from the website of the Society of Mathematicians of Serbia (in Serbian): dms.rs/informatika-osnovne-skole
A miraculous table with 3 rows and 3 columns filled with digits was found in the rainforest. Next to the table, the text of a riddle was found. The riddle says: “Form the first three-digit number from the digits on the main diagonal of the table, starting from the top and going towards the bottom. Then form another three-digit number from the digits on the anti-diagonal, also from top to bottom. The solution of the riddle is the product of these two three-digit numbers.” Write a program that reads the digits written in the table and prints the product of the two three-digit numbers formed in the described way.
Clarification: The main diagonal consists of the first digit of the first row, the middle digit of the second row and the last digit of the third row. The anti-diagonal consists of the last digit of the first row, the middle digit of the second row and the first digit of the third row.
Input
Each of the three lines of standard input contains three digits written without spaces.
Output
The program must print one integer to standard output – the product of the two three-digit numbers formed as described above.
Example
Input
123
456
789
Output
56763
Explanation
The number formed from the main diagonal is 159, and the number formed from the anti-diagonal is 357. Their product is 56763.
Task analysis
One way to solve the task is to use matrices and nested loops. Since these topics often appear at the national competition, it is recommended to know them well. Before solving this task, try the two preparatory tasks below first.
Preparatory task 1: matrix diagonals
Read 9 numbers into a 3×3 matrix. Print the values on the main diagonal and on the anti-diagonal.
Example
Input
1 2 3
4 5 6
7 8 9
Output
Main diagonal: 1 5 9
Anti-diagonal: 3 5 7
Hints and solution
Hints — solution idea
1. Matrix
We use a two-dimensional array (matrix) a[3][3] to store data in 3 rows and 3 columns.
2. Reading
The matrix is read with nested for loops: the outer loop changes rows, and the inner loop changes columns.
3. Main diagonal
The main diagonal contains the elements whose row and column indices are equal, i.e. a[i][i].
4. Anti-diagonal
On the anti-diagonal the column index is “mirrored”: for row i the column is 2 − i, i.e. a[i][2 - i]. One loop is enough, with no if condition.
C++ solution
#include <iostream>
using namespace std;
int main() {
int a[3][3];
// read the matrix: the outer loop changes rows, the inner loop columns
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> a[i][j];
}
}
cout << "Main diagonal:";
for (int i = 0; i < 3; i++) {
cout << " " << a[i][i]; // row i, column i
}
cout << endl;
cout << "Anti-diagonal:";
for (int i = 0; i < 3; i++) {
cout << " " << a[i][2 - i]; // row i, column 2 - i
}
cout << endl;
return 0;
}
Explanation: For an n × n matrix the main diagonal is a[i][i], and the anti-diagonal is a[i][n − 1 − i].
Preparatory task 2: build a number from known digits
Enter 4 integers that are the digits of a four-digit number. Build a new number whose digits are in reverse order.
Example
Input
4
5
6
7
Output
7654
Hints and solution
Hints — solution idea
1. Place values
The last entered digit becomes the thousands digit, so we multiply it by 1000. The second to last is multiplied by 100, the third from the end by 10, and the first entered digit by 1.
2. Sum
The required number is the sum of these products: 1000·d + 100·c + 10·b + a.
C++ solution
#include <iostream>
using namespace std;
int main() {
int a, b, c, d;
cin >> a >> b >> c >> d; // digits of a four-digit number
// the last entered digit becomes the thousands digit, the second to last the hundreds, etc.
int number = 1000 * d + 100 * c + 10 * b + a;
cout << number << endl;
return 0;
}
Explanation: The same principle, “digit times place value”, is used in the main task to form the three-digit numbers.
Hints and solution
Hints — solution idea
1. Reading the table
We store the table in a character matrix char grid[3][3]. The digits are written without spaces, but cin >> ch reads one character at a time, so reading works without any extra splitting. If we read the digits into an int, the whole line (e.g. 123) would be read as one number.
2. Diagonals
In row i, the main diagonal digit is grid[i][i], and the anti-diagonal digit is grid[i][2 - i].
3. Building the numbers
We build each number digit by digit, from top to bottom: number = number * 10 + digit. The digit is obtained from the character by subtracting the character zero: ch - '0'.
4. Product
Finally we print the product of the two three-digit numbers. The largest possible product is 999 · 999 = 998001, which fits in an int.
C++ solution
#include <iostream>
using namespace std;
int main() {
char grid[3][3];
// The digits are written without spaces, but cin >> char reads one character at a time
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> grid[i][j];
}
}
int mainDiag = 0; // number formed from the main diagonal digits
int antiDiag = 0; // number formed from the anti-diagonal digits
for (int i = 0; i < 3; i++) {
// the main diagonal digit in row i is in column i
mainDiag = mainDiag * 10 + (grid[i][i] - '0');
// the anti-diagonal digit in row i is in column 2 - i
antiDiag = antiDiag * 10 + (grid[i][2 - i] - '0');
}
cout << mainDiag * antiDiag << endl;
return 0;
}
Explanation: A common mistake is to read the digits into an int matrix – that works only when the digits are separated by spaces. Here the digits are read as characters, so the solution works for the input exactly as given in the task. There is also no extra output (empty lines), which would make the answer wrong at the competition.
Lenka wants to calculate the sums of squares of the odd digits of several numbers. Help Lenka by writing a program that reads one natural number less than one billion from standard input and prints the sum of the squares of its odd digits to standard output (if there are no such digits, the sum is zero). The square of a digit equals the product of that digit with itself. For example, the square of the digit 3 is 9, because 3 · 3 = 9.
Input
One natural number less than one billion is read from standard input.
Output
Print the sum of the squares of the odd digits of that number to standard output.
Example 1
Input
1234560
Output
35
Explanation
The odd digits are 1, 3 and 5: 1 + 9 + 25 = 35.
Example 2
Input
11
Output
2
Example 3
Input
888
Output
0
Explanation
There are no odd digits.
Task analysis
To extract the digits of a number, we use the operators % and / (remainder of division by 10 and integer division by 10). You also need to know how to add numbers in a loop.
Hints and solution
Hints — solution idea
1. Reading
We read the integer. The sum of squares is set to 0 before the loop starts.
2. Extracting digits
Loop while (number > 0): the rightmost digit is number % 10, and number / 10 removes that digit. The procedure is repeated until the rest of the number becomes 0.
3. Checking parity
A digit is odd if digit % 2 != 0.
4. Sum
For each odd digit we add its square digit * digit to the sum.
C++ solution
#include <iostream>
using namespace std;
int main() {
int number;
cin >> number;
int sum = 0; // sum of squares of odd digits, 0 at the start
while (number > 0) {
int digit = number % 10; // the rightmost digit
number = number / 10; // removes the rightmost digit
if (digit % 2 != 0) { // is the digit odd
sum += digit * digit;
}
}
cout << sum << endl;
return 0;
}
Explanation: The largest possible sum is 9 · 81 = 729, so the int type is more than enough.
Laza decided to create a magic sequence of numbers in the following way: he started the sequence from a given number, and each next term is obtained by adding the number of all its divisors to the previous number. For example, if a term of the sequence equals 6, the next term is 6 + 4, i.e. 10, because the number 6 has exactly 4 divisors (1, 2, 3 and 6).
Write a program that reads from standard input the first term of the sequence (0 < a1 ≤ 10000) in the first line, and a natural number N (0 < N ≤ 100) in the second line, and prints the N-th term of Laza's sequence to standard output.
Input
The first line contains the first term of the sequence a1 (0 < a1 ≤ 10000), and the second line contains a natural number N (0 < N ≤ 100).
Output
Print the N-th term of Laza's sequence.
Example 1
Input
2
4
Output
9
Explanation
a1 = 2, a2 = 4, a3 = 7, a4 = 9.
Example 2
Input
3882
93
Output
4878
Task analysis
The task is solved by moving the computation of the number of divisors of a given number into a separate function. You need to know loops and, optionally, functions.
Hints and solution
Hints — solution idea
1. Function for the number of divisors
We write a function countDivisors(n). It loops through the numbers from 1 to n and counts those that divide n (condition n % i == 0).
2. Main loop
After reading the first term, we repeat the step N − 1 times (the first term is already known): the new term is term + countDivisors(term).
3. Output
After the loop we print the last computed term.
4. Checking the example
For a1 = 2: 2 has divisors 1 and 2 (2 + 2 = 4), 4 has divisors 1, 2, 4 (4 + 3 = 7), 7 has divisors 1 and 7 (7 + 2 = 9).
C++ solution
#include <iostream>
using namespace std;
// Number of divisors of a natural number n
int countDivisors(int n) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (n % i == 0) { // i is a divisor of n
cnt++;
}
}
return cnt;
}
int main() {
int a1, n;
cin >> a1 >> n;
int term = a1; // current term of the sequence
for (int i = 1; i < n; i++) { // n - 1 steps are needed to reach the n-th term
term = term + countDivisors(term);
}
cout << term << endl;
return 0;
}
Explanation: Do not print helper data (all divisors or intermediate terms) in the function or in the main program – at the competition that would make the output wrong. To compute divisors faster, the loop can be limited to i * i <= n, counting each divisor i together with its pair n / i.
The row with number M (M = 1, 2, 3, ...) is formed by writing the natural numbers from 1 to M, and then writing the numbers in reverse order from M − 1 down to 1. Now we form an infinite sequence by taking the first of the rows above, appending the second row, then the third row, and so on. The beginning of the resulting infinite sequence looks like this:
The terms of this sequence are numbered starting from one. Write a program that finds at which position the given number n appears for the k-th time.
Input
The only line of standard input contains two integers n and k, separated by a single space (0 < n ≤ 1 000 000, 0 < k ≤ 1 000 000).
Output
The program must print one integer to standard output – the position of the k-th occurrence of the number n in the sequence.
Example 1
Input
1 1
Output
1
Example 2
Input
2 2
Output
6
Explanation
The sequence is 1 1 2 1 1 2 3 2 1 .... The number 2 appears for the first time at position 3, and for the second time at position 6.
Example 3
Input
3 3
Output
14
Task analysis
Let us find the rule. The row with number M has 2M − 1 terms, so after the first M rows the sequence has M² terms in total (1 + 3 + 5 + ... + (2M − 1) = M²).
Row M
Row content
Row length
Position of the last term in the sequence
1
1
1
1
2
1 2 1
3
4
3
1 2 3 2 1
5
9
4
1 2 3 4 3 2 1
7
16
5
1 2 3 4 5 4 3 2 1
9
25
6
1 2 3 4 5 6 5 4 3 2 1
11
36
The number n does not appear in rows smaller than n. In row n it appears exactly once (in the middle), and in every following row M > n it appears exactly twice: at the n-th place of the row and at place 2M − n.
Hints and solutions
Hints — solution idea
1. First occurrence
The first occurrence of n is in the middle of row n. There are (n − 1)² terms before that row, so the position is (n − 1)² + n. For k = 1 that is the answer.
2. Other occurrences
After row n, every row has two occurrences: the first (left) one at position (M − 1)² + n, and the second (right) one at position (M − 1)² + 2M − n, where M is the row number.
3. Which row
For k ≥ 2 let j = k − 1 be the number of occurrences after the first one. Occurrences j = 1, 2 are in row n + 1, occurrences j = 3, 4 in row n + 2 and so on, so the row is M = n + (j + 1) / 2. If j is odd, we need the left occurrence; if it is even, the right one.
4. Simulation or formula
Solution 1 goes row by row and “uses up” occurrences (two in each row), while solution 2 computes the row and the position directly with a formula, without a loop.
5. Data type
The position can be of order (1.5·106)² ≈ 2.25·1012, which does not fit in an int. That is why we use long long.
Solution 1: row by row (simulation)
#include <iostream>
using namespace std;
int main() {
long long n, k;
cin >> n >> k;
// Row M has 2M - 1 terms, so there are (M-1)^2 terms in total before row M.
// The number n appears for the first time in row n (only once, in the middle),
// and in every following row M > n it appears twice:
// at place n (from the left) and at place 2M - n.
long long row = n; // the row in which we look for the occurrence
long long remaining = k - 1; // occurrences after the first one (in row n)
while (remaining > 0) {
row++; // move to the next row, which has 2 occurrences
if (remaining >= 2) {
remaining -= 2; // skip both occurrences in this row
} else {
break; // the required occurrence is the first one in this row
}
}
long long before = (row - 1) * (row - 1); // number of terms before this row
long long position;
if (row == n) {
position = before + n; // the only occurrence, in the middle of row n
} else if (remaining == 1) {
position = before + n; // left occurrence in the row
} else {
position = before + 2 * row - n; // right occurrence in the row
}
cout << position << endl;
return 0;
}
Explanation: The loop runs at most about k / 2 times, which is fast enough for k ≤ 106. A common mistake is to use the int type – for large inputs it overflows and gives a wrong result.
Solution 2: formula
#include <iostream>
using namespace std;
int main() {
long long n, k;
cin >> n >> k;
long long position;
if (k == 1) {
// first occurrence: middle of row n
position = (n - 1) * (n - 1) + n;
} else {
long long j = k - 1; // how many occurrences after the first one
long long row = n + (j + 1) / 2; // two occurrences in every row after the n-th
long long before = (row - 1) * (row - 1);
if (j % 2 == 1) {
position = before + n; // odd j: left occurrence in the row
} else {
position = before + 2 * row - n; // even j: right occurrence in the row
}
}
cout << position << endl;
return 0;
}
Explanation: The solution runs in constant time. Check: for n = 3, k = 3 we get j = 2, M = 3 + 1 = 4, j is even, so the position is 3² + 2·4 − 3 = 14.
Write a program that finds how many integers between two given values a and b (including a and b) have an odd number of divisors.
Input
The only line of standard input contains two integers a and b, separated by a single space (0 < a ≤ b < 1015).
Output
The program must print one integer to standard output – the number of integers between a and b (including a and b) that have an odd number of divisors.
Example
Input
3 12
Output
2
Explanation
The divisors of the numbers from 3 to 12 are: 3: 1, 3; 4: 1, 2, 4; 5: 1, 5; 6: 1, 2, 3, 6; 7: 1, 7; 8: 1, 2, 4, 8; 9: 1, 3, 9; 10: 1, 2, 5, 10; 11: 1, 11; 12: 1, 2, 3, 4, 6, 12. We see that two numbers in total (4 and 9) have an odd number of divisors.
We write a function that returns the number of divisors of the given number: a loop changes the divisor d from 1 to the number itself and counts those for which the number is divisible by d. In the main part, a loop goes from a to b and counts the numbers for which the function returns an odd value. This is slow even for small limits (for b = 1015 it would take years).
Method II: optimized
Divisors come in pairs (d, x / d). A pair always contains two different numbers, except when d = x / d, i.e. when x is a perfect square. So only perfect squares have an odd number of divisors. Instead of counting divisors, we write a function that checks whether a number is a perfect square. We still go through all numbers from a to b.
Method III: fully optimized
We do not check all numbers from a to b; instead we find the smallest and the largest integer whose square lies in the interval. Then we count the integers between them. The loop has about √b ≈ 3·107 steps, which is acceptable.
Method IV: formula
There are ⌊√x⌋ perfect squares in [1, x], so there are ⌊√b⌋ − ⌊√(a − 1)⌋ of them in [a, b]. This is computed in constant time.
Be careful with square roots
The sqrt function works with real numbers and for large values it can return a result that is 1 smaller or larger than the exact one. That is why we check the obtained integer k and correct it until k² ≤ x < (k + 1)² holds. For numbers up to 1015 the products fit in long long.
Method I (brute force)
#include <iostream>
using namespace std;
// Number of divisors of x (brute force)
long long countDivisors(long long x) {
long long cnt = 0;
for (long long d = 1; d <= x; d++) {
if (x % d == 0) cnt++;
}
return cnt;
}
int main() {
long long a, b;
cin >> a >> b;
long long counter = 0;
for (long long x = a; x <= b; x++) {
if (countDivisors(x) % 2 == 1) { // odd number of divisors
counter++;
}
}
cout << counter << endl;
return 0;
}
Explanation: Correct, but very slow – good enough only for the smallest test cases.
Method II (perfect square check)
#include <iostream>
#include <cmath>
using namespace std;
// Only perfect squares have an odd number of divisors
bool isPerfectSquare(long long x) {
long long k = (long long)sqrtl((long double)x);
// fix a possible rounding error
while (k * k > x) k--;
while ((k + 1) * (k + 1) <= x) k++;
return k * k == x;
}
int main() {
long long a, b;
cin >> a >> b;
long long counter = 0;
for (long long x = a; x <= b; x++) {
if (isPerfectSquare(x)) counter++;
}
cout << counter << endl;
return 0;
}
Explanation: Faster than the first method, but it still checks all numbers up to b.
Method III (squares only)
#include <iostream>
#include <cmath>
using namespace std;
// The largest integer k such that k * k <= x
long long intSqrt(long long x) {
long long k = (long long)sqrtl((long double)x);
while (k * k > x) k--;
while ((k + 1) * (k + 1) <= x) k++;
return k;
}
int main() {
long long a, b;
cin >> a >> b;
// the smallest k with k * k >= a
long long firstK = intSqrt(a);
if (firstK * firstK < a) firstK++;
// the largest k with k * k <= b
long long lastK = intSqrt(b);
// we check only the squares from firstK * firstK to lastK * lastK
long long counter = 0;
for (long long k = firstK; k <= lastK; k++) {
counter++; // k * k is in the interval [a, b]
}
cout << counter << endl;
return 0;
}
Explanation: It goes only through the numbers k whose square is in the interval.
Method IV (formula)
#include <iostream>
#include <cmath>
using namespace std;
long long intSqrt(long long x) {
if (x <= 0) return 0;
long long k = (long long)sqrtl((long double)x);
while (k * k > x) k--;
while ((k + 1) * (k + 1) <= x) k++;
return k;
}
int main() {
long long a, b;
cin >> a >> b;
// There are intSqrt(b) squares in [1, b] and intSqrt(a - 1) squares in [1, a - 1]
cout << intSqrt(b) - intSqrt(a - 1) << endl;
return 0;
}
Explanation: The most efficient solution: constant time.
Do you remember “The Mallow-Coloured Garden” (Bašta sljezove boje), the wonderful book by Branko Ćopić, and the adventures of little Baja, the saddler Petrak and Baja's grandfather? Little Baja has several cards, and on each card an integer between 1 and 10 is written. Baja and old Petrak set off on a trip to the Moon. In the meantime grandfather Rade, who does not see very well, arranged the cards in a row.
When Baja returned from the unsuccessful trip to the Moon, he started dealing the cards. In each deal he wanted to arrange the received cards from the smallest to the largest (if some cards repeat, they are placed next to each other). Help Baja by writing a program that calculates how many times Baja made a mistake and did not arrange the cards as he wanted.
Input
The first line of standard input contains an integer N (1 < N < 100) – the number of deals Baja played, then the number k (2 < k < 10) – the number of cards in each deal, and then for each deal k cards listed in the order in which Baja arranged them. Each number is given in a separate line.
Output
In a single line of standard output the program should print the number of deals in which Baja did not arrange the cards correctly.
Example
Input
4
3
1
2
3
4
3
4
1
5
5
8
8
8
Output
1
Explanation
Data for 4 deals (3 cards each) was read. The first, third and fourth deals are arranged correctly. The second deal, with cards 4, 3, 4, is not.
The outer loop goes through the N deals, and the inner one reads the k cards. We compare each card with the previous card of the same deal. If the current card is smaller than the previous one, the deal is wrong. It is important to read all remaining cards of that deal even after a mistake is found, so that the input is read correctly.
Method II: vectors
We read the cards of one deal into a vector. We make a copy of the vector and sort only the copy. If the copy differs from the original, the original was not sorted, so Baja made a mistake.
Counting
In both methods we increase the counter of wrong deals by 1 every time a deal is not arranged in non-decreasing order.
Method I (nested loops)
#include <iostream>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
int wrongDeals = 0;
for (int deal = 0; deal < n; deal++) {
int previous;
cin >> previous; // the first card of the deal
bool ok = true;
for (int i = 1; i < k; i++) {
int current;
cin >> current; // read all cards, even after a mistake
if (current < previous) { // not non-decreasing
ok = false;
}
previous = current;
}
if (!ok) wrongDeals++;
}
cout << wrongDeals << endl;
return 0;
}
Explanation: Non-decreasing order means that every card is greater than or equal to the previous one, which is why a mistake is detected with the condition current < previous.
Method II (vectors)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
int wrongDeals = 0;
for (int deal = 0; deal < n; deal++) {
vector<int> cards(k);
for (int i = 0; i < k; i++) cin >> cards[i];
vector<int> sortedCards = cards; // copy of the original sequence
sort(sortedCards.begin(), sortedCards.end()); // sort only the copy
if (sortedCards != cards) { // different => Baja made a mistake
wrongDeals++;
}
}
cout << wrongDeals << endl;
return 0;
}
Explanation: Comparing two vectors with the != operator compares them element by element.
Dino the Dinosaur is enjoying a tournament organized by a well-known internet browser. You remember the dinosaur game, i.e. the game of collecting cacti. Every participant may collect consecutive cacti arranged along a straight line. The state committee has to determine the threshold for a contestant to advance from the first level of the game to the next one, looking only at the number of collected cacti. A large number of contestants take part in this game.
Dino keeps the score table, and contestants keep asking him how many contestants would advance if the lower threshold were this many points (all participants whose number of points is greater than or equal to the threshold advance). Dino decided to write a program that answers these questions.
Input
The number of contestants n (1 ≤ n ≤ 50000) is read from standard input, followed by the contestants' points (natural numbers), given in sorted order from the largest to the smallest and separated by spaces. Then the number m (1 ≤ m ≤ 50000) is read, the number of questions Dino has to answer, followed by m numbers separated by spaces; for each of them we need to answer how many contestants would advance if that number were taken as the lower threshold.
Output
Print the required numbers of advancing contestants to standard output, each threshold in a separate line.
Example
Input
5
79 63 63 46 13
4
85 40 60 5
Output
0
4
3
5
Explanation
If the threshold is 85 points, nobody advances. If the threshold is 40 points, the contestants with 79, 63, 63 and 46 points advance. If the threshold is 60 points, the contestants with 79, 63 and 63 points advance. If the threshold is 5 points, all contestants advance.
Hints and solutions
Hints — solution idea
1. Reading
We read n and the points (in decreasing order), then m and the threshold for each question.
2. What we are looking for
Since the points are sorted from the largest to the smallest, the advancing contestants form the beginning of the array. The required number is the index of the first position where the number of points is smaller than the threshold (or n if there is no such position).
3. Linear search
For each question we start from the beginning of the array and count contestants while points[i] >= threshold. In the worst case that is n · m = 2.5·109 steps.
4. Binary search
Because the array is sorted, we can halve it in every step: if the middle element is ≥ threshold, all elements to its left also advance, so we look for the boundary to the right; otherwise to the left. Each question then takes O(log n).
Solution 1: linear search
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> points(n); // points are sorted from the largest to the smallest
for (int i = 0; i < n; i++) cin >> points[i];
int m;
cin >> m;
for (int q = 0; q < m; q++) {
int threshold;
cin >> threshold;
// linear search: count contestants with points >= threshold
int number = 0;
while (number < n && points[number] >= threshold) {
number++;
}
cout << number << "\n";
}
return 0;
}
Explanation: Simple, but slower in the worst case.
Solution 2: binary search
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> points(n); // decreasing order
for (int i = 0; i < n; i++) cin >> points[i];
int m;
cin >> m;
for (int q = 0; q < m; q++) {
int threshold;
cin >> threshold;
// binary search for the first position where points[position] < threshold
int lo = 0, hi = n; // the required index is in [lo, hi]
while (lo < hi) {
int mid = (lo + hi) / 2;
if (points[mid] >= threshold) {
lo = mid + 1; // everything left of mid advances too, search further to the right
} else {
hi = mid; // mid is already below the threshold
}
}
cout << lo << "\n"; // lo is also the number of advancing contestants
}
return 0;
}
Explanation: At the end of the loop, lo is the first position with points smaller than the threshold, which is also the number of contestants who advance.
The heights of all students of a school are known. Write a program that finds the tallest boy and the tallest girl in the school, determines which of them is taller and prints the difference of their heights.
Input
The number of students (5 ≤ n ≤ 100) is entered from standard input, and then each of the next n lines contains data for one student. For each student, the height (an integer between 120 and 200) and the gender mark (m for male and z for female – from the Serbian word ženski) are entered, separated by a single space. Assume that there is at least one boy and at least one girl.
Output
If the tallest girl is taller than the tallest boy, print z, a space and then how much taller she is. If the tallest boy is taller than the tallest girl, print m, a space and then how much taller he is. If they are of the same height, print only =.
Example
Input
5
152 z
174 m
165 z
172 m
170 z
Output
m 4
Explanation
The tallest boy is 174 tall and the tallest girl is 170, so the boy is taller by 4.
Hints and solution
Hints — solution idea
1. Reading
We read n and in a loop read the height (int) and the gender (char) n times.
2. Two maximums
We keep the greatest height of the boys and, separately, the greatest height of the girls. For each student, depending on the gender, we update the corresponding maximum.
3. Comparison
At the end we compare the two maximums and print the required message: the gender mark of the taller student and the difference, or the = sign.
C++ solution
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int tallestBoy = 0, tallestGirl = 0;
for (int i = 0; i < n; i++) {
int height;
char gender;
cin >> height >> gender;
if (gender == 'm') {
if (height > tallestBoy) tallestBoy = height;
} else if (gender == 'z') {
if (height > tallestGirl) tallestGirl = height;
}
}
if (tallestBoy > tallestGirl) {
cout << "m " << tallestBoy - tallestGirl << endl;
} else if (tallestGirl > tallestBoy) {
cout << "z " << tallestGirl - tallestBoy << endl;
} else {
cout << "=" << endl;
}
return 0;
}
Explanation: The initial value 0 for both maximums is safe because all heights are greater than 120.
The array a contains the lengths of the n chapters of a book. Element a0 is the number of pages of the first chapter, element a1 of the second, and so on. The book has to be split into two parts so that the total numbers of pages of the two parts differ as little as possible. The split is made after one of the chapters. If there are two possible splits with the same smallest difference, the split is made after the earlier of the two chapters (so that the first part is shorter). Write a program that determines after which chapter the split should be made.
Input
The first line of standard input contains a natural number n (1 < n ≤ 50000). Each of the next n lines contains one natural number (between 1 and 1000) – the number of pages of each chapter.
Output
Print in one line of standard output the number of the chapter (chapters are numbered from zero) after which the book should be split.
Example
Input
5
100
120
50
150
70
Output
1
Explanation
When the split is made after chapter number 1 (the second chapter), the first part of the book contains 220 pages and the second 270 pages, which gives the smallest possible difference of 50 pages. A split after chapter number 2 (the third chapter) would also give a difference of 50 (270 and 220), but according to the task the first part has to be the shorter one, so the answer is 1.
Task analysis
Let left[i] be the number of pages in chapters 0 to i, and right[i] the number of pages in all chapters after chapter i. For the example from the task we get:
i
0
1
2
3
4
chapter a[i]
100
120
50
150
70
left[i]
100
220
270
420
490
right[i]
390
270
220
70
0
right[i] − left[i]
290
50
−50
−350
−490
We are looking for the split with the smallest absolute value in the last row. The values decrease until they cross from positive to negative, and after that they grow in absolute value, so the minimum is always around that crossing. When two values are equal in absolute value (columns 1 and 2), the earlier one is taken.
Hints and solution
Hints — solution idea
1. Total number of pages
We read the chapters into an array and at the same time add up the total number of pages total.
2. Difference for one split
If the first part (chapters 0 to i) has leftPages pages, the second part has total − leftPages. The difference is |total − 2·leftPages|.
3. Best split
For each i from 0 to n − 2 (there is no split after the last chapter) we increase leftPages and compute the difference. We remember the smallest difference and the chapter where it was obtained.
4. Equal differences
We change the chapter only when the difference is strictly smaller than the smallest one so far. That way, in case of a tie the earlier chapter stays, as the task requires.
C++ solution
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> pages(n);
int total = 0;
for (int i = 0; i < n; i++) {
cin >> pages[i];
total += pages[i];
}
int leftPages = 0; // number of pages in chapters 0..i
int minDiff = total + 1;
int bestChapter = 0;
// the split is made after chapter i, so the last possible split is after n - 2
for (int i = 0; i < n - 1; i++) {
leftPages += pages[i];
int rightPages = total - leftPages;
int diff = abs(rightPages - leftPages);
// strictly smaller difference: in case of a tie the earlier one stays (shorter first part)
if (diff < minDiff) {
minDiff = diff;
bestChapter = i;
}
}
cout << bestChapter << endl;
return 0;
}
Explanation: No helper arrays or special cases are needed: one loop and a running sum are enough. The solution handles equal differences correctly (the earlier chapter stays) and runs in O(n) time.
Write a program that, among the entered numbers, finds those whose digits are strictly increasing, looking from the most significant digit (the first digit from the left).
Input
The number N (1 ≤ N ≤ 5000) is read from standard input, followed by N natural numbers greater than or equal to 10 and less than or equal to 109, each in a separate line.
Output
Print the numbers with increasing digits to standard output, each in a separate line.
Example
Input
3
123
222
321
Output
123
Hints and solutions
Hints — solution idea
Idea
The digits are strictly increasing from left to right when every digit is strictly smaller than the digit to its right. Equivalently, looking from right to left, the digits are strictly decreasing.
Solution 1: digits from the right
We extract the digits starting from the last one (x % 10), and x /= 10 removes it. We remember the previous (right) digit; at the start it is 10, which is greater than any digit. If the current digit is greater than or equal to the previous one, the digits are not increasing and we stop the loop. This way there is no need to count the digits.
Solution 2: string
We read the number as a string. Neighbouring characters are compared with the <= operator: digit characters are ordered the same way as the digits, so the comparison is correct.
Solution 1: digits from the right
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int number;
cin >> number;
// We go from the least significant digit (from right to left). The digits are strictly increasing
// from the left if they are strictly decreasing from the right.
int x = number;
int previous = 10; // greater than any digit
bool increasing = true;
while (x > 0) {
int digit = x % 10;
if (digit >= previous) { // the left digit is not smaller than the right one
increasing = false;
break;
}
previous = digit;
x /= 10;
}
if (increasing) {
cout << number << "\n";
}
}
return 0;
}
Explanation: An older approach with the functions log10 and pow (number of digits and a power of 10) works with real numbers and is unnecessarily complicated. Extracting digits from the right needs neither.
Solution 2: number as a string
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string number;
cin >> number; // read the number as a string of digits
bool increasing = true;
for (size_t j = 1; j < number.size(); j++) {
if (number[j] <= number[j - 1]) { // digits are compared as characters
increasing = false;
break;
}
}
if (increasing) {
cout << number << "\n";
}
}
return 0;
}
Explanation: Short and clear. Since all numbers are ≤ 109, there are no problems with type limits.
A school is choosing actors for a school play in which the characters have a big difference in height. Write a program that determines in how many ways we can choose two actors from a class so that their height difference equals the given number r.
Input
First a positive natural number r (1 ≤ r ≤ 1000) is entered from standard input, in the next line the number of students n (1 ≤ n ≤ 50000), and after that, in the next n lines, the height of each student (a number between 1 and 100000).
Note. In 20 of the 25 test cases all children have different heights.
Output
Print the number of pairs that can be formed to standard output.
Example 1
Input
10
5
150
160
165
170
175
Output
3
Explanation
Pairs can be formed from the first and second child (150, 160), the second and fourth child (160, 170) and the third and fifth child (165, 175).
Example 2
Input
23
5
157
180
157
162
134
Output
4
Explanation
Pairs can be formed from the first and second child (157, 180), the fifth and first (134, 157), the second and third (157, 180) and the fifth and third child (134, 157).
Task analysis
If the heights are read into an array, it is a good idea to sort it first. For every pair of heights we need to check whether their difference equals r. This can be done with two nested loops or more efficiently: for a fixed first height, the second one is found with binary search, or the heights are counted in advance.
Hints and solutions
Hints — solution idea
Solution 1: two loops
We sort the heights in increasing order. For each height v[i] we go through the following heights v[j]. If the difference equals r, we increase the number of pairs; if it is greater than r, we stop the inner loop because all following differences will be even greater. For test cases with many equal heights this can be slow.
Solution 2: binary search
We sort the heights. For each height v[i] we look for how many elements are equal to v[i] + r. Since the array is sorted, these elements form one continuous block, and the function equal_range finds its beginning and end with binary search. We add the number of elements in the block to the result. This also counts repeated heights correctly.
Solution 3: counting
Heights are limited to 100000, so we can count how many students have each height in the array number[h]. For each height h, the number of pairs (h, h + r) is number[h] · number[h + r]. We add up the result over all heights. No sorting is needed.
Data type
The number of pairs can be greater than 109 (e.g. if half of the students have one height and the other half a height greater by r), so the counter should be a long long.
Solution 1: two loops
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int r, n;
cin >> r >> n;
vector<int> v(n);
for (int i = 0; i < n; i++) cin >> v[i];
sort(v.begin(), v.end()); // heights in increasing order
long long pairs = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
int diff = v[j] - v[i];
if (diff == r) {
pairs++;
} else if (diff > r) {
break; // all further differences are even greater
}
}
}
cout << pairs << endl;
return 0;
}
Explanation: The simplest solution, but quadratic in the worst case.
Solution 2: binary search
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int r, n;
cin >> r >> n;
vector<int> v(n);
for (int i = 0; i < n; i++) cin >> v[i];
sort(v.begin(), v.end());
long long pairs = 0;
for (int i = 0; i < n; i++) {
// using binary search we find all elements equal to v[i] + r
// (they are always to the right of i, because r >= 1)
auto range = equal_range(v.begin(), v.end(), v[i] + r);
pairs += range.second - range.first; // how many of them there are
}
cout << pairs << endl;
return 0;
}
Explanation: A common mistake is to find children of the same height by moving an index manually, which can read an element outside the array. The function equal_range does this safely. The complexity is O(n log n).
Solution 3: counting by heights
#include <iostream>
#include <vector>
using namespace std;
int main() {
int r, n;
cin >> r >> n;
const int LIMIT = 100000; // the greatest possible height
vector<long long> number(LIMIT + 1, 0); // number[h] = how many students have height h
for (int i = 0; i < n; i++) {
int h;
cin >> h;
number[h]++;
}
// For each height h, there are number[h] * number[h + r] pairs (h, h + r)
long long pairs = 0;
for (int h = 1; h + r <= LIMIT; h++) {
pairs += number[h] * number[h + r];
}
cout << pairs << endl;
return 0;
}
Explanation: The solution runs in linear time O(n + maximum height). Since r ≥ 1, a pair never consists of the same student.
The prices of all items sold in a shop are known. A customer has a certain amount of money and wants to buy items that are as expensive as possible. He takes the items one by one starting from the most expensive, while he has money. If he does not have enough money for the most expensive one, he takes the most expensive one he can afford.
Note. This strategy does not guarantee that the items the customer buys will have the greatest possible total value (for example, if he has 5 dinars and the items cost 4, 3 and 2 dinars, he will buy only the item for 4 dinars, although he could buy the items for 3 and 2 dinars).
Write a program that determines how much money the customer has left after shopping in the described way.
Input
The first line of standard input contains the amount of money (an integer) the customer has, the second line the number of items N (a natural number less than 50000), and the next line contains the prices of the items, separated by spaces.
Output
Print the remaining amount of money after shopping in the described way to standard output.
Example 1
Input
1250
5
1010 357 725 1125 115
Output
10
Explanation
The customer buys the item for 1125 (125 is left), then he cannot buy 1010, 725 and 357, and he buys the item for 115 (10 is left).
Example 2
Input
10000
6
3010 3005 5725 1265 2075 385
Output
0
Task analysis
The algorithm of the task is shown in the figure. The array of prices is sorted in decreasing order. At the start, the remainder equals the money the customer has. Then the array is traversed in order: if the price of an item is less than or equal to the current remainder, the item is “bought”, i.e. the price is subtracted from the remainder. Otherwise the item is skipped.
Figure 1: Preparation for the national competition 2018: task “Shopping” – algorithm (labels in Serbian)
Hints and solution
Hints — solution idea
1. Sorting
We sort the prices in decreasing order, so that we process them from the most expensive to the cheapest. In C++ this is done with sort and the comparison function greater<int>().
2. Traversal
For each item we check whether its price is less than or equal to the remaining money. If it is, we subtract it from the remainder.
3. Output
After going through all items we print the remainder.
C++ solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
int money, n;
cin >> money >> n;
vector<int> prices(n);
for (int i = 0; i < n; i++) cin >> prices[i];
// from the most expensive to the cheapest
sort(prices.begin(), prices.end(), greater<int>());
int remaining = money;
for (int i = 0; i < n; i++) {
if (prices[i] <= remaining) { // if there is enough money, the item is bought
remaining -= prices[i];
}
}
cout << remaining << endl;
return 0;
}
Explanation: Each item is considered exactly once and bought at most once, and since the array is in decreasing order, the customer never misses a more expensive item he could have bought.
Write a program that prints all binary sequences (sequences containing only zeros and ones) of length n that do not contain two consecutive ones. Print the sequences in lexicographic order (so that the binary numbers formed from these sequences are in increasing order).
Input
The first line of standard input contains a natural number n (n ≤ 10).
Output
Print the required binary sequences to standard output in lexicographic order, each sequence in a separate line.
Example
Input
3
Output
000
001
010
100
101
Task analysis
The task can be solved with a recursive function that forms the binary sequences. The function has one parameter – the index i of the position we are currently filling. If the initial value is i = 0, the next call has i = 1, and so on.
In each call we try to write a zero and to write a one at position i. A zero can always be written. A one may be written only if the previous element is not a one (according to the task there must not be two consecutive ones). In both cases we call the function for the next position i + 1. When i = n is reached, the sequence is complete and it is printed. Since a zero is always written first and only then a one, the sequences are produced in lexicographic order.
Figure 2: Preparation for the national competition 2018: task “Binary sequences” – tree of recursive calls
Hints and solution
Hints — solution idea
1. Recursive function
The function build(i) fills position i, while positions 0..i − 1 are already filled.
2. Base case
When i == n, all elements are set: we print the sequence and return.
3. Branching
First we write 0 and call build(i + 1). Then, if i == 0 or the previous element is 0, we write 1 and call build(i + 1) again.
4. Order
Because we always try zero first and then one, the sequences are printed in increasing (lexicographic) order.
C++ solution
#include <iostream>
using namespace std;
int n;
int seq[10];
// Print the formed binary sequence
void printSeq() {
for (int i = 0; i < n; i++) {
cout << seq[i];
}
cout << "\n";
}
// Fills position i; positions 0..i-1 are already filled
void build(int i) {
if (i == n) { // the sequence is complete
printSeq();
return;
}
seq[i] = 0; // a zero can always be written
build(i + 1);
// a one is allowed only if the previous element is not a one
if (i == 0 || seq[i - 1] == 0) {
seq[i] = 1;
build(i + 1);
}
}
int main() {
cin >> n;
build(0);
return 0;
}
Explanation: Watch the output format: the digits are printed without spaces (000), not as 0 0 0, which would not match the task. Pointers are not needed either – a global array is enough.
The value of a company's stocks changed from day to day over a long period of time. Write a program that determines how many sequences of consecutive days there are in which the total sum of changes of the stock value equals the given number z.
Input
The first line of standard input contains the number z (an integer between −10000 and 10000), the next line the number of days n during which the changes were tracked (3 ≤ n ≤ 50000), and the next line the changes for each day compared to the previous day (integers between −100 and 100), separated by spaces.
Output
Print the number of periods in which the total change equals z (i.e. the number of non-empty segments of the array of changes whose sum equals z) to standard output.
Example
Input
11
10
1 2 3 5 1 -1 1 5 3 2
Output
7
Explanation
The following segments have the sum 11: (1 2 3 5), (1 2 3 5 1 −1), (2 3 5 1), (2 3 5 1 −1 1), (5 1 −1 1 5), (1 −1 1 5 3 2), (1 5 3 2).
Task analysis
The simplest approach is to check every segment (two loops), but that is O(n²), which for n = 50000 means about 1.25 billion additions. It is solved more efficiently with prefix sums.
Hints and solutions
Hints — solution idea
Solution 1: all segments
We fix the start i, then in a loop increase the end j and add change[j] to the running sum. Every time the sum equals z, we increase the counter. This passes the smaller tests.
Solution 2: prefix sums
Let P[j] be the sum of the first j changes (P[0] = 0). The sum of the segment from day i + 1 to day j is P[j] − P[i]. The segment has sum z when P[i] = P[j] − z.
Counting
As we go through the days, we maintain a map seen that stores, for each prefix sum value, how many times it has appeared so far. For each j we add seen[P[j] − z] to the result, and then record P[j] as well. At the start seen[0] = 1 (the empty prefix).
Data type
The number of segments can be about 1.25·109, which exceeds the limit of the int type, so the counter must be a long long.
Solution 1: all segments
#include <iostream>
#include <vector>
using namespace std;
int main() {
int z, n;
cin >> z >> n;
vector<int> change(n);
for (int i = 0; i < n; i++) cin >> change[i];
long long counter = 0;
for (int i = 0; i < n; i++) { // start of the period
int sum = 0;
for (int j = i; j < n; j++) { // end of the period
sum += change[j];
if (sum == z) counter++;
}
}
cout << counter << endl;
return 0;
}
Explanation: Simple, but with O(n²) complexity.
Solution 2: prefix sums and a map
#include <iostream>
#include <map>
using namespace std;
int main() {
int z, n;
cin >> z >> n;
// prefix[j] = sum of the first j changes. Period (i, j] has the sum prefix[j] - prefix[i].
// So for each prefix we count how many earlier prefixes have the value (prefix - z).
map<int, int> seen;
seen[0] = 1; // empty prefix (before the first day)
int prefix = 0;
long long counter = 0;
for (int i = 0; i < n; i++) {
int change;
cin >> change;
prefix += change;
auto it = seen.find(prefix - z);
if (it != seen.end()) {
counter += it->second;
}
seen[prefix]++;
}
cout << counter << endl;
return 0;
}
Explanation: The complexity is O(n log n). For the example from the task (z = 11) it gives 7.
National competition 2016
TASK 152016
Divisible
National competition7th and 8th grade (2nd category)
In every task the time limit is 1 second and the memory limit is 64 MB.
Two integers a, b (0 < a < b < 1015) and an integer c (1 < c < 1015) are given. Write a program (console application) DELJIVI that prints how many integers between a and b (including a and b) are divisible by the given number c.
Input
The first line of standard input contains a natural number a, the second line a natural number b, and the third line a natural number c.
Output
Print the required number in the only line of standard output.
Example 1
Input
8
40
5
Output
7
Explanation
The numbers divisible by 5 are 10, 15, 20, 25, 30, 35 and 40.
Example 2
Input
8
40
8
Output
5
Example 3
Input
8
1234567890
8
Output
154320986
Task analysis
The simplest solution is to check, in a loop, the divisibility of every number from a to b by c. That is not efficient enough (because of the time limit it does not pass all test cases). It is somewhat better to continue in steps of c once the first number divisible by c is found. The most efficient way is to compute the count with a formula.
Hints and solutions
Hints — solution idea
Solution 1: all numbers
In a loop from a to b we check the condition i % c == 0 and count. For b up to 1015 that is far too many steps.
Solution 2: steps of c
The first multiple of c that is ≥ a is (a + c − 1) / c * c. From it we go in steps of c up to b and count. For a small divisor c (e.g. 2) there are still too many steps.
Solution 3: formula
Among the numbers from 1 to x there are x / c multiples of c (integer division). So the number of multiples in [a, b] is b / c − (a − 1) / c. It is computed in constant time.
Data type
The numbers reach 1015, so a 64-bit type is needed, e.g. unsigned long long or long long.
Reading from a file (while testing)
While practising, you can read the examples from a file instead of standard input if you add freopen("deljivi.01.in", "r", stdin); at the beginning of main (and #include <cstdio>). Remove this line before submitting the solution.
Solution 1: all numbers
#include <iostream>
using namespace std;
int main() {
unsigned long long a, b, c;
cin >> a >> b >> c;
unsigned long long counter = 0;
for (unsigned long long i = a; i <= b; i++) { // check every number
if (i % c == 0) {
counter++;
}
}
cout << counter << endl;
return 0;
}
Explanation: Far too slow for large limits.
Solution 2: steps of c
#include <iostream>
using namespace std;
int main() {
unsigned long long a, b, c;
cin >> a >> b >> c;
// the first multiple of c that is not smaller than a
unsigned long long i = (a + c - 1) / c * c;
unsigned long long counter = 0;
while (i <= b) {
counter++;
i += c; // the next multiple is greater by c
}
cout << counter << endl;
return 0;
}
Explanation: Good when c is large, but slow when it is small.
Solution 3: formula
#include <iostream>
using namespace std;
int main() {
unsigned long long a, b, c;
cin >> a >> b >> c;
// There are x / c multiples of c in [1, x] (integer division).
// In [a, b] there are: (those in [1, b]) - (those in [1, a - 1]).
unsigned long long result = b / c - (a - 1) / c;
cout << result << endl;
return 0;
}
Explanation: Check: 40 / 5 − 7 / 5 = 8 − 1 = 7. A common mistake is to compute the first multiple with an expression that fails in edge cases, so that you have to check separately whether a is already divisible by c. This approach has no special cases.
National competition 2024
The tasks are taken from Petlja, from the website takprog.petlja.org. To check your solutions, log in to Petlja and click the link for the corresponding grade.
Two little travellers have four heavy bags in total. Each traveller can carry at most two bags, one in each hand. All bags are shared and any traveller can carry any bag. If the bags are too heavy, our travellers want to use as little help from others as possible. The weight of each bag is known, as well as the greatest load each traveller can carry. Determine the smallest total weight of the bags they cannot carry.
Input
The first line of standard input contains four natural numbers separated by single spaces – the weights of the bags. The second line contains two natural numbers separated by a single space – the weights the little travellers can carry. All numbers are less than 1000.
Output
Print one non-negative integer to standard output – the smallest total weight of the bags for which the travellers need help.
Example 1
Input
1 2 3 4
5 6
Output
0
Explanation
One traveller can carry the bags weighing 1 and 4, and the other the bags weighing 2 and 3, so they need no help.
Example 2
Input
2 3 4 5
5 6
Output
3
Explanation
One traveller can carry the bag weighing 5, and the other the bags weighing 2 and 4, so they need help with the bag weighing 3.
Example 3
Input
12 20 15 17
17 14
Output
35
Example 4
Input
99 56 34 22
3 4
Output
211
Explanation
No bag is light enough for the travellers, so they need help with all bags: 99 + 56 + 34 + 22 = 211.
Example 5
Input
7 5 4 2
7 9
Output
2
Explanation
The first traveller carries the bag weighing 7, and the second the bags weighing 5 and 4 (9 in total). The bag weighing 2 is left.
We read the weights of the four bags and the carrying capacities of the two travellers.
2. Recursion
The function solve(i, cap1, cap2, bags1, bags2) processes the bag with index i. The parameters cap1 and cap2 are the remaining capacities of the travellers, and bags1 and bags2 the numbers of bags they already carry (at most 2).
3. Three possibilities
For bag i we consider: (1) nobody carries it (its weight is added to the “not carried” part); (2) the first traveller carries it – if he has a free hand and enough capacity; (3) the second traveller carries it, under the same conditions. The function returns the smallest total load that is left uncarried.
4. End of recursion
When all four bags are processed (i == 4), there is no more uncarried load, so we return 0.
5. Number of possibilities
There are at most 34 = 81 ways to distribute the bags, so such a complete search (trying all possibilities) is very fast.
C++ solution
#include <iostream>
#include <algorithm>
using namespace std;
int weight[4]; // weights of the bags
// Minimum weight of the bags that will not be carried, if bags 0..i-1 are already distributed.
// cap1, cap2 - remaining capacity of the travellers, bags1, bags2 - number of bags they already carry
int solve(int i, int cap1, int cap2, int bags1, int bags2) {
if (i == 4) {
return 0; // all bags are processed
}
// 1) nobody carries bag i (help from others is needed)
int best = weight[i] + solve(i + 1, cap1, cap2, bags1, bags2);
// 2) the first traveller carries the bag (if he has a free hand and enough capacity)
if (bags1 < 2 && weight[i] <= cap1) {
best = min(best, solve(i + 1, cap1 - weight[i], cap2, bags1 + 1, bags2));
}
// 3) the second traveller carries the bag
if (bags2 < 2 && weight[i] <= cap2) {
best = min(best, solve(i + 1, cap1, cap2 - weight[i], bags1, bags2 + 1));
}
return best;
}
int main() {
for (int i = 0; i < 4; i++) cin >> weight[i];
int p1, p2;
cin >> p1 >> p2;
cout << solve(0, p1, p2, 0, 0) << endl;
return 0;
}
Explanation: The option “nobody carries the bag” is essential: when a bag cannot be carried (or it is better that nobody carries it), its weight goes into the result.
Đole is a quantum physicist who designed an experiment as part of his doctoral studies. Đole is trying to bring a certain particle into a stable position at room temperature. To check whether he succeeded, he measures the speed of the particle over time. For the particle to be considered stable, its speed must oscillate during some k consecutive moments, i.e. during those k moments the speed must alternately increase and decrease. Đole is a brilliant physicist, but not such a good programmer, so he asked you to determine, based on the sequence of measurements, whether the experiment succeeded.
Input
The first line of standard input contains two natural numbers n and k (2 ≤ k ≤ n ≤ 106). The second line contains n positive real numbers separated by spaces. These numbers are the speeds of the particle in each of the n moments of the experiment.
Output
In the first line of standard output print yes or no, depending on whether the experiment succeeded. In the second line print the length of the longest run of moments in which the speed of the particle oscillated.
Note: the original task (in Serbian) uses the words da / ne; here they are translated.
There is no run of 7 consecutive elements in which the speed oscillates. The longest such run has 6 elements: 6.9, 7.8, 4.3, 17.11, 2.6, 5.8.
Example 2
Input
10 4
6.8 5.3 4.1 9.17 3.7 2.1 1.0 9.9 17.64 2.1
Output
yes
4
Explanation
The run of consecutive moments 5.3, 4.1, 9.17, 3.7 satisfies the condition.
Example 3
Input
7 4
2.2 7.5 3.5 3.5 5.7 9.9 10.0
Output
no
3
Explanation
There is no run of 4 consecutive elements in which the speed oscillates. The run 2.2, 7.5, 3.5, 3.5 does not satisfy the condition because the last two elements are equal.
Hints and solution
Hints — solution idea
1. What oscillation is
A run oscillates if the direction of change keeps switching: up, then down, then up... Equal neighbouring elements break the oscillation (there is neither an increase nor a decrease).
2. Direction of change
For every two neighbouring measurements we determine the direction: +1 if the speed increases, −1 if it decreases, 0 if it stays the same.
3. Length of the current run
We keep the length current of the current oscillating run and the direction of the last change. If the new direction is 0, the length goes back to 1. If the new direction is opposite to the previous one, the length grows by 1. In all other cases (same direction, or there was no change before) the run starts from the previous element, so the length is 2.
4. Longest run
We keep the greatest length longest. If longest ≥ k, we print yes, otherwise no; in the second line we print longest.
5. Speed
There can be up to a million numbers, so we turn on faster input: ios::sync_with_stdio(false); cin.tie(nullptr);. There is no need to store the whole array – the previous value is enough.
C++ solution
#include <iostream>
using namespace std;
int main() {
ios::sync_with_stdio(false); // fast input, because there are up to a million numbers
cin.tie(nullptr);
int n, k;
cin >> n >> k;
double previous;
cin >> previous;
int current = 1; // length of the current oscillating run
int longest = 1;
int dir = 0; // direction of the last change: +1 up, -1 down, 0 none
for (int i = 1; i < n; i++) {
double x;
cin >> x;
int newDir; // direction of the change previous -> x
if (x > previous) newDir = 1;
else if (x < previous) newDir = -1;
else newDir = 0;
if (newDir == 0) {
current = 1; // equal neighbours break the oscillation
} else if (dir != 0 && newDir == -dir) {
current++; // the direction changed: the oscillation continues
} else {
current = 2; // new start: previous and x form a run of length 2
}
dir = newDir;
previous = x;
if (current > longest) longest = current;
}
cout << (longest >= k ? "yes" : "no") << "\n" << longest << "\n";
return 0;
}
Explanation: The solution processes the sequence in a single pass, so the complexity is O(n) and the memory O(1).
A popular sneaker shop, Patikarnica, decided to offer a new service to its customers. A touch screen will be placed at the entrance, suggesting to customers the most expensive sneaker models that fit the budget they planned for new sneakers. Patikarnica bought all the equipment needed for the new service, but asked us to write a program that, based on the sneakers available in the shop and the money a customer is willing to spend, shows the list of the most expensive models the customer can afford.
Patikarnica has n different sneaker models in stock, each described by its name and price. During the day, k customers enter the shop. At the entrance, each customer enters the amount of money x they want to spend on new sneakers, and the system shows them the list of the most expensive sneakers they can afford.
Input
The first line contains the number n (1 ≤ n ≤ 105) of different sneaker models offered by Patikarnica, then each of the next n lines contains the name of a model (a string of at most 200 characters) and its price (a natural number less than 50000). After that the number k (1 ≤ k ≤ 106) of customers who entered the shop is read, and then, in the next k lines, the budgets xi (1 ≤ i ≤ k) of individual customers (natural numbers less than 50000).
Output
For each of the k customers and their budgets xi, print to standard output a lexicographically sorted list of the most expensive sneakers they can afford (name and price, in the same line). If there are no sneakers the customer can afford, print the string none.
Note: the original task (in Serbian) prints the word nema; here it is translated.
The first customer entered a budget of 18000 dinars, and the most expensive sneakers they can afford are AdidasXPLRBoost, AsicsSkzElite and NikeJordanMaxAura5 at the price of 17199 dinars. The second customer entered a budget of 12000 dinars and there are no sneakers they can afford, so the word none is printed.
Hints and solution
Hints — solution idea
1. Sneaker data
Prices are less than 50000, so we can group the sneakers by price: for each price c we keep a vector of the names of the sneakers that cost exactly c (vector<vector<string>>).
2. Sorting the names
The names in each group are sorted lexicographically.
3. The most expensive affordable price
For every possible budget x we compute in advance bestPrice[x] – the greatest price ≤ x for which there is at least one model (or −1 if there is none). This is done in a single pass: if there are sneakers with price x, the value is x; otherwise it is the value for x − 1.
4. Queries
For each customer, one array access gives us the price: if it is −1, we print none; otherwise we print all names from that price group.
5. Alternative
The sneakers can also be sorted by price, and then for each budget binary search finds the most expensive model that is not more expensive than the budget. Counting by prices is simpler because the prices are small.
6. Speed
There are up to a million queries, so we turn on fast input and output and avoid endl (we use "\n").
C++ solution
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false); // fast input and output because of the large number of queries
cin.tie(nullptr);
const int LIMIT = 50000; // prices are less than 50000
vector<vector<string>> byPrice(LIMIT); // byPrice[c] = names of the sneakers with price c
int n;
cin >> n;
for (int i = 0; i < n; i++) {
string name;
int price;
cin >> name >> price;
byPrice[price].push_back(name);
}
for (int c = 0; c < LIMIT; c++) {
sort(byPrice[c].begin(), byPrice[c].end()); // lexicographic order within the same price
}
// bestPrice[x] = the greatest price <= x for which there is at least one model (or -1)
vector<int> bestPrice(LIMIT, -1);
for (int x = 0; x < LIMIT; x++) {
if (!byPrice[x].empty()) bestPrice[x] = x;
else if (x > 0) bestPrice[x] = bestPrice[x - 1];
}
int k;
cin >> k;
for (int q = 0; q < k; q++) {
int budget;
cin >> budget;
if (budget >= LIMIT) budget = LIMIT - 1;
int price = bestPrice[budget];
if (price == -1) {
cout << "none\n";
} else {
for (const string &name : byPrice[price]) {
cout << name << " " << price << "\n";
}
}
}
return 0;
}
Explanation: Each query is answered in constant time (plus printing the result). The solution is also correct when several sneakers have the same price, because all of them are printed, sorted lexicographically.
When a key on the keyboard is pressed, the first letter of every word in the text is moved to the last place of that word. How many times does the key have to be pressed for the sentence to return to its initial state?
Input
One line of text is read from standard input; it consists of lowercase letters of the English alphabet and spaces and has at most 106 characters. All test cases are such that the sentence returns to its initial state after at most 1018 key presses.
In 50% of the test cases the words are not periodic. In 60% of the test cases all words are shorter than 100 characters.
Output
Print to standard output the smallest number of key presses (greater than 0) needed for the sentence to return to its initial state.
Example 1
Input
mala sirena zivi na dnu mora
Output
12
Explanation
Pressing the key gives the following sentences: mala sirena zivi na dnu mora, alam irenas iviz an nud oram, lama renasi vizi na udn ramo, amal enasir iziv an dnu amor, mala nasire zivi na nud mora, ..., and after 12 presses the initial sentence is obtained. (The sentence is in Serbian: “the little mermaid lives at the bottom of the sea”.)
Each word is rotated by one letter on every key press, independently of the others. The whole sentence returns to its initial state when all words return to their initial states at the same time.
2. One word
A word of length L returns to its initial state after L presses, but maybe earlier if it is periodic. For example, the word abcabc has length 6, but after 3 rotations the same word is obtained, so its number of presses is 3. The number of presses is the smallest period of the word if it divides the length of the word; otherwise it is the length of the word.
3. Smallest period
We compute the smallest period with the prefix function (KMP algorithm): if pi is the length of the longest proper prefix that is also a suffix of the word, the period is L − pi. If L is not divisible by the period, the word is not periodic and the number of presses is L. For short words (up to 100 letters) simply checking all divisors of the length is enough.
4. All together
If word 1 needs p1 presses, word 2 needs p2 and so on, the sentence is in its initial state when the number of presses is divisible by all pi, and the smallest such number is their LCM (least common multiple). We compute the LCM using the GCD (Euclid's algorithm): LCM(a, b) = a / GCD(a, b) * b.
5. Data type
The result can be up to 1018, so we use long long. We divide before multiplying to avoid overflow.
C++ solution
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
long long gcdOf(long long a, long long b) {
while (b != 0) {
long long rem = a % b;
a = b;
b = rem;
}
return a;
}
// The smallest number of moves of the first letter to the end after which the word returns to its initial state.
// It is the smallest period of the word if it divides the length of the word; otherwise it is the whole length.
int wordPeriod(const string &r) {
int len = r.size();
vector<int> pi(len, 0); // prefix function (KMP)
for (int i = 1; i < len; i++) {
int j = pi[i - 1];
while (j > 0 && r[i] != r[j]) j = pi[j - 1];
if (r[i] == r[j]) j++;
pi[i] = j;
}
int p = len - pi[len - 1]; // smallest period
if (len % p == 0) return p;
return len;
}
int main() {
string line;
getline(cin, line);
istringstream input(line);
long long result = 1; // LCM of the periods of all words
string word;
while (input >> word) {
long long p = wordPeriod(word);
result = result / gcdOf(result, p) * p;
}
cout << result << endl;
return 0;
}
Explanation: Be careful: the word abcabc has length 6 (not 4), and its number of presses is 3. Check for the first example: LCM(4, 6, 4, 2, 3, 4) = 12.
National competition 2023
The tasks are taken from Petlja, from the website takprog.petlja.org. To check your solutions, log in to Petlja and click the link for the corresponding grade.
A sequence is even-odd (even-odd-even-odd...) if the even positions of the sequence (0, 2, 4, ...) contain only even numbers, and the odd positions (1, 3, 5, ...) contain only odd numbers. Write a program that checks for several sequences whether they are even-odd sequences.
Input
The numbers n (5 ≤ n ≤ 100) and k (5 ≤ k ≤ 100) are read from standard input, and then, from the next n lines, n sequences of k elements each, with values between 0 and 1000.
Output
For each sequence, print yes to standard output if it satisfies the condition, and no otherwise.
Note: the original task (in Serbian) uses the words da / ne; here they are translated.
Example
Input
3 5
1 2 3 4 5
2 3 4 5 6
4 7 0 9 2
Output
no
yes
yes
Explanation
In the first sequence, position 0, which is even, contains the number 1, which is odd. In the second sequence, the even positions contain 2, 4 and 6, which are even, and the odd positions contain 3 and 5, which are odd. In the third sequence, the even positions contain 4, 0 and 2, which are even, and the odd positions contain 7 and 9, which are odd.
The outer loop goes through the n sequences, and the inner one through the k elements of one sequence.
2. Condition for one element
Element x at position j is correct if x and j have the same parity, i.e. x % 2 == j % 2. Indeed: an even position (j % 2 == 0) must contain an even number (x % 2 == 0), and an odd position an odd number.
3. Reading to the end of the line
Even when we find that a sequence is not correct, the remaining elements of that sequence must be read, so that the next sequence is read correctly. That is why we use a boolean variable valid instead of breaking the loop.
4. Output
After the inner loop we print yes or no.
C++ solution
#include <iostream>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) { // n sequences
bool valid = true;
for (int j = 0; j < k; j++) { // k elements of a sequence
int x;
cin >> x; // read all elements, even after a mistake
// even positions must contain even numbers, odd positions odd numbers:
// the remainder of division by 2 must equal the remainder of the position
if (x % 2 != j % 2) {
valid = false;
}
}
cout << (valid ? "yes" : "no") << "\n";
}
return 0;
}
Explanation: In the original Serbian statement the sequence was called “ppnn”, while the analysis page uses the name “pnpn” (from parno-neparno, even-odd). The real condition of the task is alternating even-odd.
Write a program that implements a Serbian–English dictionary. First a list of words and their translations is read, and then the user asks the system to translate some words (either from Serbian to English or from English to Serbian).
Input
The number n (1 ≤ n ≤ 50000) of word pairs in English and Serbian is entered from standard input (in each line first the English word and then the Serbian word, as in the example). All Serbian words are different from each other and all English words are different from each other. After that m (1 ≤ m ≤ 50000) queries are entered. Each query contains the target language (sr or en) and then a word in the other language that has to be translated.
Output
Print all translations to standard output. If no translation was entered for a word, print ?.
Example
Input
3
cat macka
dog pas
mouse mis
4
en pas
sr mouse
sr lion
en mis
Output
dog
mis
?
mouse
Explanation
The query en pas asks for the English translation of the Serbian word pas: dog. The query sr mouse asks for the Serbian translation of the English word mouse: mis. There is no translation for the word lion.
Hints and solution
Hints — solution idea
1. Two maps
We keep two dictionaries: toSerbian (English word → Serbian word) and toEnglish (Serbian word → English word). For every pair we read, we store both directions.
2. Queries
If the target language is en, the word is Serbian, so we look it up in the map toEnglish. If the target language is sr, the word is English, so we look it up in the map toSerbian.
3. Unknown word
We search with the find function. If the word is not in the map, we print ?. We do not use the [] operator for searching, because for an unknown word it would add a new (empty) element.
4. Speed
We use unordered_map (constant access time on average) or an ordinary map (logarithmic) – both are fast enough for 50000 words and queries.
C++ solution
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
unordered_map<string, string> toSerbian; // English word -> Serbian word
unordered_map<string, string> toEnglish; // Serbian word -> English word
for (int i = 0; i < n; i++) {
string english, serbian;
cin >> english >> serbian; // in the example the English word comes first, then the Serbian one
toSerbian[english] = serbian;
toEnglish[serbian] = english;
}
int m;
cin >> m;
for (int i = 0; i < m; i++) {
string lang, word;
cin >> lang >> word; // lang is the language we translate into
if (lang == "en") {
auto it = toEnglish.find(word); // word is Serbian
cout << (it == toEnglish.end() ? "?" : it->second) << "\n";
} else {
auto it = toSerbian.find(word); // word is English
cout << (it == toSerbian.end() ? "?" : it->second) << "\n";
}
}
return 0;
}
Explanation: The original statement said that the pairs are entered “in Serbian and English”, but the example shows that each line contains the English word first and then the Serbian one. That is how it is written here.
Quiz: test your knowledge
Quiz – preparation for the national competition
1. Which elements of the matrix a[3][3] form the anti-diagonal?
2. In the task “Rainforest”, what is the easiest way to read digits written without spaces (e.g. 123)?
3. Which numbers have an odd number of divisors?
4. Why do we use long long in the task “Position of the K-th occurrence of a number”?
5. What is necessary for binary search to work correctly?
6. How many integers divisible by c are there in the interval [a, b]?
7. In the task “Stocks” a segment has the sum z. What do we look for in the prefix sums P?
8. In the task “Letter rotator”, how is the final answer for the whole sentence obtained?
9. In the task “Binary sequences” a one may be written at position i only if…
10. Why does the strategy from the task “Shopping” (always take the most expensive item you can) not always give the best result?
11. In the task “Dino the Dinosaur”, how can the questions be answered faster when the points are sorted?
12. In the task “Translation”, what is a good choice for storing word → translation pairs?
Podešavanja kolačića
Koristimo kolačiće da bismo vam pružili najbolje moguće iskustvo. Takođe nam omogućavaju da analiziramo ponašanje korisnika kako bismo stalno poboljšavali veb lokaciju za vas.