Problem guide

By Liviu, tweaks by Alex Vasiluță

Table of contents

  1. Attachments
  2. Statements
  3. Tests and configuration
  4. Checker
    1. Sample standard checker
    2. Sample legacy checker
  5. Grader
    1. Grader sample 1
    2. Grader sample 2
    3. Grader sample 3
    4. Grader sample 4

Attachments

Attachments are files associated with a problem. They are the backbone of a problem.

Some uses for attachments are:

  • Special attachments influence the display and the execution behaviour of the problem
  • Images to attach onto the problem markdown
  • Provide sample code for interactive problems

Attachments of the following formats are considered special and are displayed in the Problem statement section:

  • statement-{language}.{format}
  • statement-{language}-{type}.{format} - Advanced, experimental, not yet recommended

Where:

  • language: Display language, can be any of en or ro.
  • format: Display type of statement, can be md (Markdown) or pdf.
  • type: Advanced type of statement, this is an experimental new feature. Should be llm (for AI translated statements), short (for short variants of statements).

Attachments of the following formats are considered special if their exec checkbox is toggled:

  • grader.{extension}-> used for interactive problems
  • *.h -> used for interactive problems or to just give the user access to functions/classes for ease of use
  • checker.{extension}-> will be used as checker
  • checker_legacy.{extension} -> will be used as checker
  • .output_only-> only raw text submissions can be submitted

Accepted extensions are: cpp for C++ (you can enforce a specific version by using cpp20, cpp17), c, py, pas.

Attachment properties

All attachments have the following properties:

  • Visible: Attachment that can be seen in the sidebar of a problem.
  • Private: Attachment that cannot be seen by the user
  • Exec: Attachment will be considered for execution, either as a checker or a grader resource.

Do note that an attachment can be neither Visible nor Private, as is the case with images attached in a statement, and cannot be both simultaneously.

Also note that the existence of both visible and private attachments can be seen via an API request. Only non-private attachments can, though, be downloaded by a user.

Statements

  • There must always be a Markdown version of a statement for at least one language and, preferably, for all languages in which the problem was originally provided (limited to English and Romanian, for now). Statements written in Romanian must use diacritics
  • For Markdown statements, LaTeX is used to render formulas, e.g. i=1nab+cdefi2\displaystyle \sum_{i=1}^{n} \frac{a^{b+c}}{d^e-f_i} \le \sqrt{2} is $\displaystyle \sum_{i=1}^{n} \frac{a^{b+c}}{d^e-f_i} \le \sqrt{2}$ ; use \displaystyle for big formulas. A list of supported features can be found here

Formatting tips

  • You can make text bold or italic using Markdown (text is *text*, text is **text**, text is ***text***). You can underline, strikethrough, or make coloured text using LaTeX (YES NO\text{\underline{YES} }\color{red}\sout{NO} is $\text{\underline{YES} }\color{red}\sout{NO}$). Always use \text{} when you intend to write text instead of formulas; a mod ba\text{ mod }b (a\text{ mod }b) instead of a mod ba\ mod\ b (a\ mod\ b).
  • You can create links using [text](url) and attach images using ![caption, optional](image link, uploaded as attachment). This is standard Markdown syntax.
  • However, to display images more conveniently you can use ~[image.png], where image.png is an attachment of the current problem. You can also change the alignment (centre or right) or width (in percentages or pixels): ~[image.png|align=center|width=50%]

Styling conventions

  • Variables, equations and numbers must be written using LaTeX and not placed inside a markdown code block (NN numbers instead of N numbers)
  • An exception to the above rule is when you mention file names (sum.in is `sum.in` in markdown) or function names in interactive problems (compare). You should also use code blocks if you want to emphasise strings or specific characters, usually for output formatting (ABABAAABAA; ,, ., * represent cells; NO).
  • There should be a space AFTER each punctuation sign (,, :, etc.), and NOT BEFORE it. (1, 2, ..., n as opposed to 1,2,...,n or 1 , 2 , ... , n)
  • Every group of 3 digits must be separated by a space (1 000 0001 \ 000 \ 000 instead of 1,000,0001,000,000; 1 \ 000 \ 000 in LaTeX)
  • Replace asterisks * which stand for multiplication with \cdot (\cdot) or \times (×\times)

Tests and configuration

The tests (including the score/setting files) must be uploaded in a .zip file not exceeding 512MB.

You can use CMS style grouping parameters to configure the subtasks at this point, especially helpful if you're importing problems from CMS. There's also support, albeit limited, for importing Polygon archives.

Naming format

  • {test_id}-{suffix}.{extension}
  • {prefix}.{test_id}.{extension}
  • {test_id}.{extension}
  • grader_test{test_id}.{extension}

Each test must have an input (corresponding with a .in extension) and output file (corresponding with any of the .out, .ok, .sol extensions).

Test Scores File

Alongside the tests you can upload a .txt file specifying the individual score for each test. If left out, the score is distributed evenly across all tests. This step should be skipped if you're using subtasks for scoring.

Each line corresponds to a test and should have the following format:
{test_id} {test_score}

Problem Settings File

Alongside the tests you can upload a .properties file to set problem parameters

# Setup subtasks
groups = 0,1-5,6-10,11-18,19-30
weights = 0,10,10,25,55
# Include subtasks in other subtasks
# For example Subtask 2 includes 1, Subtask 3 includes 1 and 2, and Subtask 5 includes 1,2,3,4
dependencies = ,1,1;2,,1-3;4
time = 1.200
memory = 512

Submissions & Attachments

You have the ability to directly upload attachments straight from the zip file, by placing the files you wish to include in a folder called attachments.
You can also automatically upload submissions, by including the source files in a folder called submissions.

Checker

For problems with multiple correct answers, the checker is run to determine the score of the user. The run time of the checker is not added to the execution time for a submission.

  • Must be named checker.cpp or checker_legacy.cpp and added as a private attachement with the exec toggle checked.
  • By default compiles with C++11, if you use a newer C++ version, just use the relevant extension (.cpp14, .cpp17, .cpp20).
  • Other languages are supported for use in checkers, however strongly discouraged by the admin team.
  • You can check the contestant's source code in a special file called contestant.txt, created in the checker's directory. This can be used disallow certain keywords.
  • The standard checker will receive as arguments the file name of the input, correct output and user output in this order.
  • The standard checker will print to stderr the output message and to stdout a float between 0 and 1 proportional to the score received
  • The legacy checker will print to stdout an integer between 0 and 100, the percentage of the score received and then the output message.
  • There are output messages that can be automatically translated for the user.
    • translate:success -> Correct
    • translate:partial -> Partially correct
    • translate:wrong -> Wrong answer

Sample standard checker (CMS & others)

#include <bits/stdc++.h>

using namespace std;

float score = 0;

void result(const char* msg, float pts) {
    //"{score}" to stdout
    //"{message}" to stderr
    // score is a float between 0 and 1
	printf("%f", pts);
    fprintf(stderr, "%s", msg);
	exit(0);
}

void Assert(bool cond, string str) {
	if (!cond)
		result(str.c_str(), 0);
}
void Success(float pts, string str) {
	result(str.c_str(), pts);
}

ifstream out, ok, in;

int main(int argc, char* argv[]) {
	in.open(argv[1]); // test input; assumed to be valid
	ok.open(argv[2]); // correct output
	out.open(argv[3]); // user output
    int task;
	in >> task;
    if(task == 1){
        int ansok, ansout;
        ok >> ansok;
        Assert(!!(out >> ansout), "translate:wrong"); // try to read contestant's answer
        Assert(ansout == ansok, "translate:wrong"); // check if values match
        score += 1;
		Success(score, "translate:success");
    }
    else {
        int nrok, nrout;
        // not giving partial points if the first part of the answer is wrong, even if the second one might be right
        Assert(!!(out >> nrout), "Wrong answer. Wrong format for number");
        ok >> nrok;
        Assert(nrout == nrok, "Wrong answer. Incorrect number");
        score += 0.5;
        string message = "Correct number. ";
        double valok, valout;
        ok >> valok;
        // giving partial points even if the second part of the answer is wrong
        if(!!(out >> valout) && abs(valok - valout) <= 1e-5)
            score += 0.5, message += "Correct value";
        else
            message += "Wrong value";
        Success(score, message);
    }
}

Sample legacy checker (Old OJI)

#include <bits/stdc++.h>

using namespace std;

int score = 0;

void result(const char* msg, int pts) {
    // "{score} {message}" to stdout
    // score is an integer between 0 and 100
	printf("%d %s", pts, msg);
	exit(0);
}

void Assert(bool cond, string str) {
	if (!cond)
		result(str.c_str(), 0);
}
void Success(int pts, string str) {
	result(str.c_str(), pts);
}

ifstream out, ok, in;

int main(int argc, char* argv[]) {
	out.open(argv[1]); // user output
	ok.open(argv[2]); // correct output
	in.open(argv[3]); // test input; assumed to be valid
    int task;
	in >> task;
    if(task == 1){
        int ansok, ansout;
        ok >> ansok;
        Assert(!!(out >> ansout), "translate:wrong"); // try to read contestant's answer
        Assert(ansout == ansok, "translate:wrong"); // check if values match
        score += 100;
        Success(score, "translate:success");
    }
    else {
        int nrok, nrout;
        // not giving partial points if the first part of the answer is wrong, even if the second one might be right
        Assert(!!(out >> nrout), "Wrong answer. Wrong format for number");
        ok >> nrok;
        Assert(nrout == nrok, "Wrong answer. Incorrect number");
        score += 50;
        string message = "Correct number. ";
        double valok, valout;
        ok >> valok;
        // giving partial points even if the second part of the answer is wrong
        if(!!(out >> valout) && abs(valok - valout) <= 1e-5)
            score += 50, message += "Correct value";
        else
            message += "Wrong value";
        Success(score, message);
    }
}

Grader

  • For interactive problems where you only want the user to implement specific functions, the grader contains the main function that runs the functions needed to be implemented.
  • Keep in mind the run time is added to the execution time of the program. You usually need a custom checker for interactive problems, so make sure to separate the code for the checker and the grader accordingly.
  • Must be named grader.cpp/grader.pas/etc. (see Attachments) and added as a private attachment with the exec toggle checked.
  • Setting a specific version to grader.cpp (for instance, .cpp14) limits C++ versions the user can submit to ones \geq the one specified.
  • Interactive problems require a grader for each language they intend to support. Pascal support is experimental and other languages are currently untested and most likely broken.
  • It can include other header files included as public attachments (if they contain functions the user must implement/use) with the exec toggle checked.
  • It is common practice to include a sample grader as a public attachment to make local testing easier. Keep in mind it must not have the exec toggle.
  • A simple grader, using a header file, is illustrated in Sample 1;
  • A header file is necessary if the user uses a function without defining it, otherwise it can be omitted as in Sample 2.
  • If the input the grader reads must remain a secret, you can either read it from a file and overwrite it before you call any of the user's functions OR read it from stdin and then overwrite it (for example using freopen) as illustrated in Sample 3
  • In some situations, to preserve the integrity of the function calls, you might want to print a secret message (random string or hash of input file) using the grader and check it before reading anything else using the custom checker. This is usually done where the checker only determines how many times a function is called like in Sample 4.

Sample 1

grader.cpp

#include <bits/stdc++.h>
#include "sum.h"

using namespace std;

int main() {
    int a, b;
    cin >> a >> b;
    cout << sum(a, b);
}

sum.h

int sum(int a, int b);

user

#include "sum.h"

int sum(int a, int b) {
    return a + b;
}

Sample 2

grader.cpp

#include <bits/stdc++.h>

using namespace std;

int sum(int a, int b);

int main() {
    int a, b;
    cin >> a >> b;
    cout << sum(a, b);
}

user


int sum(int a, int b) {
    return a + b;
}

Sample 3

grader.cpp

#include "compare.h"
#include <bits/stdc++.h>

using namespace std;

static int x;

int solve(); // could've been defined in compare.h instead

int compare(int y) {
	return y > x;
}

int main() {
	cin >> x;
	freopen("mumbojumbo", "r", stdin); // overwrite stdin before any user interaction to prevent it from reading the number
	cout << solve(); // we don't need to prevent the user from writing anything else, because the checker will check the number itself which is secret
}

compare.h

int compare(int);

user

#include "compare.h"

int solve() {
	int left = 1, right = 1e9;
	while (left < right) {
		int m = (left + right+1) / 2;
		if (compare(m))
			right = m - 1;
		else
			left = m;
	}
	return left;
}

Sample 4

grader.cpp

#include <bits/stdc++.h>

using namespace std;

static vector<int> a;
static int op = 0;

void solve(int n); // user implemented, doesn't have access to the array

bool compare(int x, int y) {// used by the user to analyse the array, included in a header file
	assert(0 <= x && x < n && 0 <= y && y < n);
	return a[x] < a[y];
}

void swap(int x, int y) { // called by user to sort the array, included in a header file
	assert(0 <= x && x < n && 0 <= y && y < n);
	swap(a[x], a[y]);
	++op;
}

int main() {
	int n;
	cin >> n;
	a.assign(n);
	for (auto &x: a)
		cin >> x;
	freopen("haha", "r", stdin);
	solve(n);
	cout << "PIEZISA"; 	// secret string to prevent the user from doing cout << 1; exit(0)
							// will check in custom checker if the first string is PIEZISA
	for(int i = 1; i < n; ++i)
		assert(a[i - 1] < a[i]);
	cout << op; 			// checker will determine score based on this
}
Updated at: 1714954718805 Posted at: 1688675068513