#include <bits/stdc++.h>
using namespace std;
 
// Структура для дней
struct Day {
    int a, b, c, d;
};
 
// Структура для событий по оси p
// type = -1: в момент p = a, день переходит из S (не покрытых) в покрытые (удаляем интервал)
// type = +1: в момент p = b+1, день переходит из покрытых в S (добавляем интервал)
struct Event {
    int p, type, c, d;
};
 
// Сегментное дерево с поддержкой range add и запроса максимума с индексом
struct SegTree {
    int n;
    vector<int> lazy;
    // tree[node] = {maxValue, index}
    vector<pair<int,int>> tree;
 
    SegTree(int n) : n(n) {
        lazy.assign(4*n, 0);
        tree.assign(4*n, {0, 0});
    }
 
    void build(int idx, int l, int r) {
        if(l == r) {
            tree[idx] = {0, l};
            return;
        }
        int mid = (l + r) / 2;
        build(idx*2, l, mid);
        build(idx*2+1, mid+1, r);
        tree[idx] = max(tree[idx*2], tree[idx*2+1]);
    }
 
    void push_down(int idx, int l, int r) {
        if(lazy[idx] != 0) {
            int mid = (l + r) / 2;
            // Обновляем детей
            tree[idx*2].first += lazy[idx];
            lazy[idx*2] += lazy[idx];
 
            tree[idx*2+1].first += lazy[idx];
            lazy[idx*2+1] += lazy[idx];
 
            lazy[idx] = 0;
        }
    }
 
    void update(int idx, int l, int r, int ql, int qr, int val) {
        if(ql > r || qr < l)
            return;
        if(ql <= l && r <= qr) {
            tree[idx].first += val;
            lazy[idx] += val;
            return;
        }
        push_down(idx, l, r);
        int mid = (l + r) / 2;
        update(idx*2, l, mid, ql, qr, val);
        update(idx*2+1, mid+1, r, ql, qr, val);
        tree[idx] = max(tree[idx*2], tree[idx*2+1]);
    }
 
    pair<int,int> query() {
        return tree[1];
    }
};
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int n;
    cin >> n;
    vector<Day> days(n);
    for (int i = 0; i < n; i++){
        cin >> days[i].a >> days[i].b >> days[i].c >> days[i].d;
    }
 
    // Собираем координаты для q (печенек) для компрессии
    vector<int> coords;
    // добавим границы 0 и 10^9, так как q можно выбрать и вне интервалов
    coords.push_back(0);
    coords.push_back(1000000000);
    for (int i = 0; i < n; i++){
        coords.push_back(days[i].c);
        coords.push_back(days[i].d);
    }
    sort(coords.begin(), coords.end());
    coords.erase(unique(coords.begin(), coords.end()), coords.end());
    int m = coords.size();
 
    // Инициализируем сегментное дерево
    SegTree seg(m);
    seg.build(1, 0, m-1);
 
    // Начальное множество S: дни, для которых p = 0 не удовлетворяет условию Искандера.
    // p=0 удовлетворяет условию, если 0 попадает в [a, b]. Т.к. a,b >= 0, это возможно, когда a==0.
    // Таким образом, S = { i : a > 0 }.
    int S_size = 0;
    for (int i = 0; i < n; i++){
        if(days[i].a > 0){
            S_size++;
            // Для дня i добавляем вклад в сегментное дерево на отрезке [c, d]
            // Найдем сжатые координаты для интервала [c, d]
            int l = int(lower_bound(coords.begin(), coords.end(), days[i].c) - coords.begin());
            int r = int(lower_bound(coords.begin(), coords.end(), days[i].d) - coords.begin());
            seg.update(1, 0, m-1, l, r, 1);
        }
    }
 
    // Формируем события по оси p.
    vector<Event> events;
    for (int i = 0; i < n; i++){
        // Если a > 0, то в момент p = a день становится покрытым (удаляем его из S)
        if(days[i].a > 0){
            // Условие: p = a, если p попадает в [0, 1e9]
            if(days[i].a <= 1000000000) {
                events.push_back({days[i].a, -1, days[i].c, days[i].d});
            }
        }
        // Если b < 1e9, то в момент p = b+1 день становится не покрытым (добавляем в S)
        if(days[i].b < 1000000000){
            int p_event = days[i].b + 1;
            if(p_event <= 1000000000) {
                events.push_back({p_event, +1, days[i].c, days[i].d});
            }
        }
    }
    // Сортируем события по p, а при равных p: сначала type = -1 (удаление), потом +1 (добавление)
    sort(events.begin(), events.end(), [](const Event &e1, const Event &e2) {
        if(e1.p == e2.p)
            return e1.type < e2.type;
        return e1.p < e2.p;
    });
 
    // Начальный кандидат: p = 0
    int best_p = 0, best_q = 0;
    // f(0) = число дней, где p в [a, b] = n - S_size
    int baseCoverage = n - S_size;
    auto curQuery = seg.query();
    int bestVal = baseCoverage + curQuery.first;
    // Если лучше, запомним q (восстанавливаем по decompress)
    if(curQuery.first > 0)
        best_q = coords[curQuery.second];
    else
        best_q = 0;
 
    // Текущая позиция p
    int cur_p = 0;
    int idx = 0;
    int E = events.size();
    while(idx < E) {
        int next_p = events[idx].p;
        // Если между cur_p и next_p не меняется S, можно рассматривать next_p
        // Обновляем p до next_p, обрабатывая все события с этим значением
        while(idx < E && events[idx].p == next_p) {
            // Обрабатываем событие
            int l = int(lower_bound(coords.begin(), coords.end(), events[idx].c) - coords.begin());
            int r = int(lower_bound(coords.begin(), coords.end(), events[idx].d) - coords.begin());
            if(events[idx].type == -1) {
                // Удаляем день из S: обновляем сегментное дерево, S_size уменьшаем
                seg.update(1, 0, m-1, l, r, -1);
                S_size--;
            } else { // type == +1
                seg.update(1, 0, m-1, l, r, +1);
                S_size++;
            }
            idx++;
        }
        cur_p = next_p;
        // Вычисляем f(cur_p) = n - S_size (кол-во дней покрытых Искандером)
        int cov = (n - S_size);
        auto qRes = seg.query();
        int total = cov + qRes.first;
        if(total > bestVal) {
            bestVal = total;
            best_p = cur_p;
            best_q = (qRes.first > 0 ? coords[qRes.second] : 0);
        }
    }
    // После обработки событий, если cur_p < 1e9, можно проверить p = 1e9, так как S не изменится.
    if(cur_p < 1000000000) {
        cur_p = 1000000000;
        int cov = (n - S_size);
        auto qRes = seg.query();
        int total = cov + qRes.first;
        if(total > bestVal) {
            bestVal = total;
            best_p = cur_p;
            best_q = (qRes.first > 0 ? coords[qRes.second] : 0);
        }
    }
 
    cout << best_p << " " << best_q << "\n";
    return 0;
}


Убери комментарии из кода