#include <bits/stdc++.h>

using namespace std;

template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) {
        in >> x;
    }
    return in;
};

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {
        out << x << ' ';
    }
    return out;
};

int n, m, d;
vector<int> p, q;

void read() {
    cin >> n >> m >> d;
    p.resize(n);
    q.resize(m);
    cin >> p >> q;
}

pair<bool, int> check(int t1, int t2) {
    vector<pair<int, int>> p_seg, q_seg;

    for(int i = 0; i < n;) {
        int start = p[i] - t1;
        int end = p[i] + t1;
        i++;
        while(i < n && p[i] - t1 <= end) {
            end = p[i] + t1;
            i++;
        }
        p_seg.push_back({start, 1});
        p_seg.push_back({end, -1});
    }

    for(int i = 0; i < m;) {
        int start = q[i] - t2;
        int end = q[i] + t2;
        i++;
        while(i < m && q[i] - t2 <= end) {
            end = q[i] + t2;
            i++;
        }
        q_seg.push_back({start, 1});
        q_seg.push_back({end, -1});
    }

    int cnt = p_seg.size() / 2 + q_seg.size() / 2, balance = 0,
        last_pos = INT_MIN, i = 0, j = 0;

    while(i < (int)p_seg.size() || j < (int)q_seg.size()) {
        int pos, delta;
        if(j >= q_seg.size() ||
           (i < p_seg.size() && (p_seg[i].first < q_seg[j].first ||
                                 (p_seg[i].first == q_seg[j].first &&
                                  p_seg[i].second < q_seg[j].second)))) {
            tie(pos, delta) = p_seg[i++];
        } else {
            tie(pos, delta) = q_seg[j++];
        }

        balance += delta;
        if(balance == 2) {
            last_pos = pos;
        } else {
            if(balance == 1 && last_pos != INT_MIN && pos - last_pos > d) {
                return {false, cnt};
            }
            last_pos = INT_MIN;
        }
    }

    return {true, cnt};
}

void solve() {
    // The first observation to the problem is that it's enough to consider the
    // O(N+M) values for t1 and t2. Then a cubic solution is to test all pairs
    // of candidates and then perform a check(t1, t2). The check can be done in
    // O(N+M) too - we create the segments from p and q, and then have "in" and
    // "out" events (+1 and -1 to a balance). We don't have to sort these events
    // as we have two sorted lists of events which we can just merge.  Then we
    // are interested if there is a segment of length >= d. This can also be
    // done linearly by always maintaining the last point that had balance < 2,
    // and whenever the balance becomes == 2 we look at that last point and
    // check the distance to it. Note that when we merge, we should prioritize
    // the "out" events first.
    // To optimize this further, we can notice that for some t1, we would
    // ideally like to select the largest possible t2 as this will make the
    // number of openings and closings as small as possible. We will also notice
    // that if we have some t1 < t1', then the corresponding optimals are t2 >=
    // t2'. This means that we can do a two pointers like approach that starts
    // from t1=candidates[0] and t2=candidates[-1], and walk down while
    // check(t1, t2) is false. Then we will have at most O(|candidates|)
    // evaluates of check. To make the implementation convenient, we should make
    // check() return the actual answer in terms of #openings too (pair<bool,
    // int>).

    vector<int> candidates_t1, candidates_t2;
    for(int i = 1; i < n; i++) {
        int val = (p[i] - p[i - 1] + 1) / 2;
        candidates_t1.push_back(val);
    }
    for(int i = 1; i < m; i++) {
        int val = (q[i] - q[i - 1] + 1) / 2;
        candidates_t2.push_back(val);
    }

    candidates_t1.push_back(1);
    candidates_t1.push_back(d);
    candidates_t1.push_back((d + 1) / 2);
    candidates_t2.push_back(1);
    candidates_t2.push_back(d);
    candidates_t2.push_back((d + 1) / 2);

    sort(candidates_t1.begin(), candidates_t1.end());
    candidates_t1.erase(
        unique(candidates_t1.begin(), candidates_t1.end()), candidates_t1.end()
    );

    sort(candidates_t2.begin(), candidates_t2.end());
    candidates_t2.erase(
        unique(candidates_t2.begin(), candidates_t2.end()), candidates_t2.end()
    );

    int best_openings = n + m + 1;
    int best_t1 = -1, best_t2 = -1;
    int j = (int)candidates_t2.size() - 1;

    for(int i = 0; i < (int)candidates_t1.size(); i++) {
        bool valid;
        int t1 = candidates_t1[i], openings = -1;

        while(j >= 0) {
            tie(valid, openings) = check(t1, candidates_t2[j]);
            if(valid) {
                break;
            }
            j--;
        }

        if(j >= 0) {
            if(openings < best_openings) {
                best_openings = openings;
                best_t1 = t1;
                best_t2 = candidates_t2[j];
            }
        }
    }

    if(best_t1 == -1) {
        cout << "No solution\n";
    } else {
        cout << best_t1 << " " << best_t2 << "\n";
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    // cin >> T;
    for(int test = 1; test <= T; test++) {
        read();
        // cout << "Case #" << test << ": ";
        solve();
    }

    return 0;
}
