# Final Stage 2025–2026, Tour 2: Full Paper

*English translation by SOTA – AI Community of the Russian original. Organisers who would like this translation removed can email sota.ai.community@gmail.com.*

All-Russian School Olympiad in Informatics 2025–2026, Final Stage<br>
“Artificial Intelligence” profile, Tour 2, Moscow, 25 March 2026

---

## A. What to Watch?

### Statement

In an online cinema, a user needs to be recommended one film out of several possible options.

For this, the system selects five candidate films for the user and sets a rule by which one of them must be chosen. We will call such a selection task a **query**.

Each query is described by the following data:

- `user_id`: the user identifier;
- `query_type`: the query type, that is, the selection rule;
- $c_1$, $c_2$, $c_3$, $c_4$, $c_5$: the identifiers of five different candidate films.

Each row of the file `queries_A.csv` specifies one query: for the given user, one of the five proposed films must be chosen.

For each query, you need to output the `item_id` of the chosen film.

### Input format

Use the files attached to the task:

1. `queries_A.csv`
2. `items_A.csv`
3. `events_A.csv`
4. `item_meta_A.json`
5. `sessions_A.json`

### Notes

The task comes with the file `baseline_A.ipynb`, which contains an example of reading the input files, filtering the queries by type for each subtask, and a detailed description of the data.

### Output format

You need to output a CSV file with the header

`query_id,item_id`

For each `query_id` belonging to the corresponding subtask, the answer must contain exactly one row. There must be no extra rows.

### Scoring

The maximum score for each subtask is 20.

A solution is considered correct if, for each `query_id` belonging to the corresponding subtask, the correct `item_id` according to the rules of the task is given.

The final score for the task is based on the **best** submission.

### A1. Subtask 1

When solving this subtask, filter the rows of `queries_A.csv`, keeping only the queries of type `favorite_genre`, and output answers only for them.

For a `favorite_genre` query, the genre of each of the five candidates is taken, and then, for this genre, the sum of the weights of all events of the given user on films of the same genre is computed, where

- `open = 1`
- `finish = 2`
- `like = 3`

The candidate for whose genre this sum is the largest is chosen.

In case of a tie:

1. the film with the smaller `duration`;
2. the film with the smaller `item_id`.

### A2. Subtask 2

When solving this subtask, filter the rows of `queries.csv`, keeping only the queries of type `actor_match`, and output answers only for them.

For each `actor_match` query, the set `watched_actors` is formed from the actors of the films that the user

1. Either watched: `finish`;
2. Or marked as liked: `like`.

Only the actors from the `actors` field of the file `item_meta_A.json` are considered.

If the `actors` field is missing, it should be treated as an empty list.

For a candidate, `score` equals the number of common elements of the sets:

the set of actors of this candidate; the set `watched_actors`.

In case of a tie, the film with the smallest `item_id` is chosen.

### A3. Subtask 3

When solving this subtask, filter the rows of `queries_A.csv`, keeping only the queries of type `next_in_session`, and output answers only for them.

For the user, consider all adjacent pairs of films $f_1 \to f_2$ extracted from all the `path` lists in the file `sessions_A.json`.

If `path` = $[x_1, x_2, \ldots, x_k]$, then the following pairs are extracted from it:

$$\begin{gathered}
x_1 \to x_2 \\
x_2 \to x_3 \\
\vdots \\
x_{k-1} \to x_k
\end{gathered}$$

For a candidate $f_2$, its score is the number of such pairs $f_1 \to f_2$ for which the user has ever finished watching the film $f_1$, that is, there is a `finish` event for it in the file `events_A.csv`.

Each occurrence of a pair in `sessions_A.json` is counted separately.

If the user has no suitable pairs, the score of every candidate is taken to be 0.

The candidate with the largest score is chosen.

In case of a tie, the film with the smallest `item_id` is chosen.

---

## B. Seismically Active Island

### Statement

Researchers from a seismological monitoring centre are analysing records of earthquakes on the island of Terramotus. For each event, data were collected at two observation stations located at opposite ends of the island. Each station has two measuring instruments, placed in different parts of the station.

The data were transmitted from the station to the laboratory over two channels: a fibre-optic cable and a wireless link. But the wireless link suffered from interference, and some time points in the data were lost.

Each instrument records 10 quantities every minute for 100 minutes.

To speed up the analysis of the archive, the researchers brought in an AI agent. The researchers wrote the prompt carelessly and gave the agent too many permissions. Then, in the course of its work, the agent for some reason renamed all the files. After that, it became unclear to the researchers which records belong to the same earthquake: the files are mixed up, and the original grouping is lost. The researchers panicked, but then pulled themselves together and turned for help to Oleg, a school student and an expert in machine learning. Help him cluster the data and repair the consequences of uncontrolled experiments with AI.

It is known that each earthquake corresponds to exactly 8 observations: 2 instruments at each of the 2 stations, and 2 transmission methods for each. You need to work out which observations belong to the same earthquake.

### Input format

The following files are attached to the task:

- `data_B.npy`, containing an array of observations of size $240 \times 10 \times 100$: 240 observations of 10 values over 100 minutes.
- `baseline_B.ipynb`: a notebook with a baseline solution of the task.
- `submission_B.csv`: an example of the solution to be submitted to the testing system.

### Output format

For checking, you must upload the archive `solution_B.zip`.

The archive must contain:

1. The file `submission_B.csv` with two columns:
   - `ID`: the number of the observation in `data_B.npy`;
   - `target`: the predicted value of the target variable.
2. The file `solution_B.ipynb`: a Jupyter Notebook with your solution.

You may add to the archive additional files needed for your solution to work. The archive must, however, contain exactly one file with the extension `.csv` and exactly one file with the extension `.ipynb`.

### Scoring

You can get up to 60 points for this task.

The data are split into a public and a private part. When you submit `submission_B.csv`, you are shown the result on the **public** part. After the end of the stage, the result will be recomputed on the **private** part. The public and private parts do not overlap.

After the end of the stage, your metric will be converted to a 60-point scale according to the following rule:

- the result of the **baseline solution**, with a value of ARI $\leq X$, is scored **0 points**;
- a result with a value of ARI $\geq Y$ is scored **60 points**;
- if the value of ARI lies between $X$ and $Y$, the number of points is computed by the linear interpolation formula:

$$\text{Score} = 60 \cdot \frac{\text{ARI} - X}{Y - X}.$$

The metric values $X$ and $Y$ will be available in the testing system.

The final score for the task is based on the **last** submission.

### Metric for evaluating the accuracy of the answer

This task uses the **ARI (Adjusted Rand Index)** metric. The more pairs of objects the participant distributes over the clusters correctly (for example, if the two objects are in different clusters and the participant also assigns them to different clusters, OR the two objects are in the same cluster and the participant also assigns them to the same cluster), the higher this metric. *ARI* takes the value 0 for a random partition into clusters and the value 1 for a perfectly correct partition, and it can take negative values for a partition that is worse than random.

Example of computing the **ARI** metric in 'Python':

```python
from sklearn.metrics import adjusted_rand_score

labels_true = [0, 0, 1, 1, 2, 2]
labels_pred = [1, 1, 0, 0, 2, 2]

ari = adjusted_rand_score(labels_true, labels_pred)
print("ARI =", ari)
```

---

## C. A Beginner's Mistake

### Statement

Masha was invited to a summer internship at a research institute that studies animal habitats. As an example of an interesting assignment that Masha will be working on, the researchers shared observations of the habitats of a new species of sloth (*Bradypus procrastinator shkolnikus*). This species was only recently separated from a previously known one. Now the scientists want to discover other potential zones where it lives.

For Masha's convenience, the data were split into `train` and `test`. Since the researchers only have their own data on locations where sloths already live, they asked the statistical agency of South America to provide data on other locations as well, in order to extend the test dataset. Each row in the data describes a location and contains measured parameters characterising the climate, the weather, and the plant and animal life. The institute's staff complained that they simply could not build a good model for this task. Masha looked at the data and immediately noticed two typical beginner's mistakes made by the institute's staff.

Help Masha build a model that, from the available data, predicts whether a location belongs to a potential habitat zone of the new species of sloth.

### Input format

The following files are attached to the task:

- `map_C.json`: a file with the following structure:

  ```json
  {
      "coastline": [...]
  }
  ```

  `coastline` is an array of coastline coordinates used for visualisation.
- `train_C.csv`: the training set. Each object contains the location features and the target variable `target` (a binary label `0` or `1`);
- `test_C.csv`: the test set. Each object contains the identifier `id` and the location features;
- `baseline_C.ipynb`: a notebook with a baseline solution of the task;
- `submission_C.csv`: an example of the solution to be submitted to the testing system.

### Output format

For checking, you must upload the archive `solution_C.zip`.

The archive must contain:

1. The file `submission_C.csv` with two columns:
   - `ID`: the identifier of the object from the test set `test`;
   - `target`: the predicted binary class label (`0` or `1`).
2. The file `solution_C.ipynb`: a Jupyter Notebook with your solution.

You may add to the archive additional files needed for your solution to work. The archive must, however, contain exactly one file with the extension `.csv` and exactly one file with the extension `.ipynb`.

### Scoring

You can get up to 60 points for this task.

The data are split into a public and a private part. When you submit `submission_C.csv`, you are shown the result on the **public** part. After the end of the stage, the result will be recomputed on the **private** part. The public and private parts do not overlap.

After the end of the stage, your metric will be converted to a 60-point scale according to the following rule:

- the result of the **baseline solution**, with a value of F1 $\leq X$, is scored **0 points**;
- a result with a value of F1 $\geq Y$ is scored **60 points**;
- if the value of F1 lies between $X$ and $Y$, the number of points is computed by the linear interpolation formula:

$$\text{Score} = 60 \cdot \frac{\mathrm{F1} - X}{Y - X}.$$

The metric values $X$ and $Y$ will be available in the testing system.

The final score for the task is based on the **last** submission.

### Metric for evaluating the accuracy of the answer

This task uses the **F1-score** metric. The participant submits binary class labels (`0` or `1`).

Let us denote:

- `TP`: the number of true positive predictions;
- `FP`: the number of false positive predictions;
- `FN`: the number of false negative predictions.

Then

$$\text{Precision} = \frac{TP}{TP + FP},$$

$$\text{Recall} = \frac{TP}{TP + FN},$$

and

$$\mathrm{F1} = \frac{2 \cdot \text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}}.$$

The value of the **F1-score** lies in the range from 0 to 1. The higher the value, the better the quality of the classification.

Example of computing the **F1-score** metric in Python:

```python
from sklearn.metrics import f1_score

score = f1_score(y_true, y_pred)
print("F1-score =", score)
```

---

## D. Humanity's Heritage

### Statement

The eruption of Mount Vesuvius in 79 AD buried in ash and destroyed the city of Pompeii and many other towns, including Herculaneum, where in 1750 an ancient Roman villa with a large number of ancient papyri was discovered. The scrolls kept in its library had been carbonised and turned into fragile black cylinders. Today, scientists are striving to read them using X-ray tomography, computer reconstruction and machine learning.

When Vadim learned about this, he thought that he too would like to take part in the decipherment. On learning that scientists are already working on restoring the text inside each individual papyrus, he realised that the next step is to restore the correct order of the papyri themselves, since ancient treatises usually consisted of several parts.

Since Vadim does not know Ancient Greek, he decided to consider an analogous problem in Russian first. For this, he took a collection of poems and left some of the poems unchanged (`train_D.json`). He split each of the remaining poems (`test_D.json`) into two parts and then shuffled all the left and right parts together. Now Vadim's task is to restore the original pairs and put the poems back together.

### Input format

The following files are attached to the task:

- `model_D.zip`: weights for a large language model.
- `baseline_D.ipynb`: a notebook with a baseline solution of the task and an example of using the large language model `model_D.zip` to obtain text embeddings
- `train_D.json`: additional poems in full;
- `test_D.json`: the shuffled left and right parts of the poems;
- `submission_D.csv`: an example of the solution that must be submitted to the testing system.

### Output format

For checking, you must upload the archive `solution_D.zip`.

The archive must contain:

1. The file `submission_D.csv` with two columns:
   - `left_id`: the ID of a left half from `test_D.json`;
   - `right_id`: the ID of a right half from `test_D.json`.
2. The file `solution_D.ipynb`: a Jupyter Notebook with your solution.

You may add to the archive additional files needed for your solution to work. The archive must, however, contain exactly one file with the extension `.csv` and exactly one file with the extension `.ipynb`.

### Scoring

You can get up to 60 points for this task.

The data are split into a public and a private part. When you submit `submission_D.csv`, you are shown the result on the **public** part. After the end of the stage, the result will be recomputed on the **private** part. The public and private parts do not overlap.

After the end of the stage, your metric will be converted to a 60-point scale according to the following rule:

- the result of the **baseline solution**, with a value of Accuracy $\leq X$, is scored **0 points**;
- a result with a value of Accuracy $\geq Y$ is scored **60 points**;
- if the value of Accuracy lies between $X$ and $Y$, the number of points is computed by the linear interpolation formula:

$$\text{Score} = 60 \cdot \frac{\text{Accuracy} - X}{Y - X}.$$

The metric values $X$ and $Y$ will be available in the testing system.

The final score for the task is based on the **last** submission.

### Metric for evaluating the accuracy of the answer

This task evaluates how correctly the left and right parts of the poems are matched.

Let:

- $N$ be the total number of left parts in `test.json`;
- $K$ be the number of correctly restored pairs, that is, pairs `(left_id, right_id)` that coincide with the true correspondence.

Then the metric is computed by the formula

$$\text{Accuracy} = \frac{K}{N}.$$

In other words, the metric shows the proportion of correctly restored poems.

The value of **Accuracy** lies in the range from 0 to 1. The higher the value, the better the quality of the solution.

Example of computing **Accuracy** in `Python`:

```python
import pandas as pd

merged = true_pairs.merge(pred_pairs, on="left_id", suffixes=("_true", "_pred"))
correct = (merged["right_id_true"] == merged["right_id_pred"]).sum()
accuracy = correct / len(merged)

print("Accuracy =", accuracy)
```

---

## E. Suspicious Cakes

### Statement

Sasha is often ill and is trying to understand why. He suspects that the problem may be related to the cakes he regularly eats.

Recently, Sasha bought a new batch of cakes, and there may be unusual ones among them that are harmful to him. Sasha very much wants to eat all the cakes, but he does not want to fall ill again. For this, Sasha needs to learn to determine which cakes differ from the rest.

Sasha is sure that suspicious cakes can be told apart visually. To learn to find them, he decided to train a convolutional neural network to analyse images of cakes. However, for training, Sasha only has "normal" cakes, which are known to be definitely not harmful to him. Sasha eats 10 kinds of cakes in total, so he trained the neural network to classify "normal" cakes into 10 classes. Now Sasha needs to work out how to use this model to find "harmful" cakes. Sasha asks for your help in finding the suspicious cakes in the new batch.

You are given images of cakes from the new batch and the weights of a pre-trained neural network. The network was trained only on "normal" cakes and solves a classification problem with 10 classes. You need to determine which cakes from the new batch differ from the "normal" ones. It is known that there are exactly 1000 suspicious cakes.

### Input format

The following files are attached to the task:

- `public_test_package_E.npz`: test images of size $32 \times 32$;
- `model_weights_E.pt`: the weights of the pre-trained CNN;
- `baseline_E.ipynb`: a baseline notebook with an example solution;
- `submission_E.csv`: an example answer file.

### Output format

For checking, you must upload the archive `solution_E.zip`.

The archive must contain:

1. The file `submission_E.csv` with two columns:
   - `id`: the index of the object, from 0 to $N - 1$;
   - `is_outlier`: your prediction:
     - `1` if the object is suspicious;
     - `0` if the object is considered normal.
2. The file `solution_E.ipynb`: a Jupyter Notebook with your solution.

The file `submission_E.csv` must contain exactly 1000 rows with the value `is_outlier = 1`.

You may add to the archive additional files needed for your solution to work. The archive must, however, contain exactly one file with the extension `.csv` and exactly one file with the extension `.ipynb`.

### Scoring

You can get up to 60 points for this task.

If the number of objects marked as suspicious in the submitted file **differs from 1000, the task is scored 0 points**.

The data are split into a public and a private part. When you submit `submission_E.csv`, you are shown the result on the **public** part. After the end of the stage, the result will be recomputed on the **private** part. The public and private parts do not overlap.

After the end of the stage, the value of the metric will be converted to a 60-point scale according to the following rule:

- the result of the **baseline solution**, with a value of Recall@K $\leq X$, is scored **0 points**;
- a result with a value of Recall@K $\geq Y$ is scored **60 points**;
- if the value of Recall@K lies between $X$ and $Y$, the number of points is computed by the linear interpolation formula:

$$\text{Score} = 60 \cdot \frac{\text{Recall@K} - X}{Y - X}.$$

The metric values $X$ and $Y$ will be available in the testing system.

The final score for the task is based on the **last** submission.

### Metric for evaluating the accuracy of the answer

This task uses the **Recall@K** metric.

The participant must mark exactly $K = 1000$ objects as suspicious. Let hits@K be the number of correctly found suspicious objects among the marked ones. Then

$$\text{Recall@K} = \frac{\text{hits@K}}{K}.$$

In other words, the more genuinely suspicious objects you find among the 1000 you select, the higher the result.
