Olympiad task " Small denominators"

Time limit: 2 seconds.
Memory limit: 512 MB.

Condition:

Two fractions are given a/b c/d with non-negative integer numerators and positive integer denominators, and some positive integer N is selected. Consider all irreducible fractions e/f with positive integer numerators and denominators not exceeding N, lying between these two fractions: a/b e/f c/d . Write them in the final sequence in the order ascending first, denominators, numerators and then: roll e1/f1 will this list sooner e2/f2 , or if f1 f2, or if f1 = f2 and e1 e2. Your goal is to output the first n fractions in this sequence (or the entire sequence if there are less than n fractions in it).

Input data format:

The first line contains six integers a, b, c, d, N, n - these two fractions, the restriction the numerator and denominator and the number of decimals that you want to plot (0 6 a 6 1018 , 1 b, c, d, N n a b c d.

Output format:

In the first line, output an integer n' - the number of fractions that you will output. In the following n' lines, print the fractions themselves in the correct order: in the i line, there must be integers ei and fi , separated by a space - mutually prime numerator and denominator of the i th fraction (1 ei , fi n' must either be n, or lie between 0 and n-1 and equal the number of irreducible fractions between a b and c d , whose numerators and denominators are positive integers and do not exceed N.

Examples:

Input: 0 1 1 1 5 10

Output:

9
1 2
1 3
2 3
1 4
3 4
1 5
2 5
3 5
4 5

Input: 55 34 68 42 90 1

Output:

1
89 55

Input: 49 33 45 30 50 239

Output:

0

My solution

I used Дерево Штерна-Броко to generate fractions.

For some reason, on most tests it gives Wrong Answer, on some Time Limit.

#include <iostream>
#include <set>

using namespace std;

class fraction {
public:
    long long numerator, denominator;

    fraction() : numerator(0), denominator(0) {}

    fraction(long long numerator, long long denominator) : numerator(numerator), denominator(denominator) {}

    bool operator==(const fraction & fr) {
        return numerator == fr.numerator && denominator == fr.denominator;
    }

    bool operator!=(const fraction & fr) {
        return numerator != fr.numerator || denominator != fr.denominator;
    }
};

bool compare(const fraction& fr1, const fraction& fr2) { // для set
    if (fr1.denominator != fr2.denominator)
        return fr1.denominator < fr2.denominator;
    return fr1.numerator < fr2.numerator;
}

bool operator>(const fraction& fr1, const fraction& fr2) {
    if (fr1.denominator != fr2.denominator)
        return (double)fr1.numerator * fr2.denominator > (double)fr2.numerator * fr1.denominator;
    return fr1.numerator > fr2.numerator;
}

bool operator<(const fraction& fr1, const fraction& fr2) {
    if (fr1.denominator != fr2.denominator)
        return (double)fr1.numerator * fr2.denominator < (double)fr2.numerator* fr1.denominator;
    return fr1.numerator < fr2.numerator;
}

ostream& operator<<(ostream& str, const fraction& fr) {
    str << fr.numerator << ' ' << fr.denominator;
    return str;
}

long long gcd(long long a, long long b) {
    while (b != 0) {
        long long tmp = a;
        a = b;
        b = tmp % b;
    }
    return a;
}

int main() {

    long long a, b, c, d, N; int n;
    cin >> a >> b >> c >> d >> N >> n;

    {
        long long gcd1 = gcd(a, b), gcd2 = gcd(c, d);
        a /= gcd1, b /= gcd1;
        c /= gcd2, d /= gcd2;
    }

    set<fraction, decltype(&compare)> fractionSet(&compare);

    // два массива для генерации дерева
    fraction* arr1 = new fraction[262144];
    fraction* arr2 = new fraction[262144];

    int len1 = 3, i2 = 0; // индексы 1 и 2 показывают отношение к arr1 и arr2

    arr1[0] = fraction(0, 1), arr1[1] = fraction(1, 1), arr1[2] = fraction(1, 0);

    {
        // по окончании этого блока в arr1 остаётся две или больше дробей, причём первая дробь минимальна и больше a/b, вторая максимальна и меньше c/d, и все дроби между ними правильно сгенерированы
        fraction sample1 = fraction(a, b), sample2 = fraction(c, d);
    label:
        if (sample1 > arr1[1]) {
            arr1[0] = arr1[1];
            arr1[1] = fraction(arr1[0].numerator + arr1[2].numerator, arr1[0].denominator + arr1[2].denominator);
            goto label;
        }
        if (sample2 < arr1[len1-2]) {
            arr1[len1-1] = arr1[len1-2];
            arr1[len1-2] = fraction(arr1[len1-3].numerator + arr1[len1-1].numerator, arr1[len1-3].denominator + arr1[len1-1].denominator);
            goto label;
        }
        if (arr1[0] != sample1)
            if (arr1[1] != sample1)
                while (true) {
                    fraction fr = fraction(arr1[0].numerator + arr1[1].numerator, arr1[0].denominator + arr1[1].denominator);
                    if (fr.numerator > N || fr.denominator > N) {
                        len1--;
                        for (int i = 0; i < len1; i++)
                            arr1[i] = arr1[i+1];
                        break;
                    }
                    if (fr < sample1)
                        arr1[0] = fr;
                    else if (fr > sample1) {
                        for (int i = len1; i > 1; i--)
                            arr1[i] = arr1[i - 1];
                        len1++;
                        arr1[1] = fr;
                    } else {
                        arr1[0] = fr;
                        break;
                    }
                } else {
                    arr1[0] = arr1[1];
                    arr1[1] = arr1[2];
                    len1--;
                }
        int tmp = len1 - 2;
        if (arr1[tmp] != sample2) {
            while (true) {
                fraction fr = fraction(arr1[tmp].numerator + arr1[tmp+1].numerator, arr1[tmp].denominator + arr1[tmp+1].denominator);
                if (fr.numerator > N || fr.denominator > N) {
                    len1--;
                    break;
                }
                if (fr > sample2)
                    arr1[tmp + 1] = fr;
                else if (fr < sample2) {
                    arr1[len1] = arr1[len1 - 1];
                    len1++;
                    arr1[++tmp] = fr;
                } else {
                    arr1[++tmp] = fr;
                    break;
                }
            }
        } else
            len1--;
    }

    if (len1 < 2) {
        cout << 0 << endl;
        exit(0);
    }
    /*
        indexForRemove убирает дроби, после генерации которых получится дробь с числителем или знаменателем больше, чем N.
        Например, при N = 5 в последовательности 1/2 2/3 1/1 он уберёт только первую дробь.
        Возможно, не самое лучшее решение по производительности.
    */
    for (int i = 0, prevLen = 0, indexForRemove = 0;; i++) {
        for (int i1 = 0;; i1++) {
            arr2[i2] = arr1[i1];
            i2++;
            if (i1 + 1 == len1)
                break;
            long long numerator = arr1[i1].numerator + arr1[i1+1].numerator;
            if (numerator > N) {
                if (indexForRemove == i2 - 1)
                    indexForRemove++;
                continue;
            }
            long long denominator = arr1[i1].denominator + arr1[i1+1].denominator;
            if (denominator > N) {
                if (indexForRemove == i2 - 1)
                    indexForRemove++;
                continue;
            }
            fraction fr = fraction(numerator, denominator);
            arr2[i2] = fr;
            fractionSet.insert(fr);
            i2++;
        }
        if (indexForRemove != 0)
            memcpy(arr2, arr2 + indexForRemove, i2 -= indexForRemove);
        if (i2 < 2)
            break;
        fraction* tmp = arr1;
        arr1 = arr2;
        arr2 = tmp;
        len1 = i2;
        i2 = 0;
        prevLen = len1;
        indexForRemove = 0;
    }

    int limit = fractionSet.size() < n ? fractionSet.size() : n;

    cout << limit;
    set<fraction, decltype(&compare)>::iterator it = fractionSet.begin();
    for (int i = 0; i < limit; i++, it++)
        cout << '\n' << *it;
    cout << endl;
}

Here is a variant with brute force fractions. Almost all the tests are again WA, some TL, however, the correct answers have become more)
Am I missing something?

int main() {

    long long a, b, c, d, N; int n;
    cin >> a >> b >> c >> d >> N >> n;

    long long* r = new long long[400'000];
    int pos = 0, i = a + 1, j = b;
    double f = (double)a / b, s = (double)c / d;

    while (n && j <= N) {
        int lim = min(N + 1, (long long)ceil(s * j));
        while (i < lim) {
            if (gcd(i, j) == 1) {
                r[pos++] = i;
                r[pos++] = j;
                if (--n == 0)
                    break;
            }
            i++;
        }
        i = ceil(f * ++j);
    }

    cout << pos / 2 << '\n';
    for (int i = 0; i < pos; i++) {
        cout << r[i] << ' ';
        cout << r[++i] << '\n';
    }
}
Author: Имя Фамилия, 2019-12-29

1 answers

You need a fast algorithm for enumerating all irreducible fractions with a given denominator. Each such fraction is encoded by the Stern-Brokaw tree

The series of irreducible fractions itself is a Farey series. Here is an example of generating a Farey comment from the site geeksforgeeks.

Actually, the given code can be easily modified for your task.

 1
Author: becouse, 2020-01-01 17:50:31