Problem: cf1080_f
Difficulty: easy
Categories: data structures, search
Time limit: 3500 ms, memory limit: 512 MB

It is a very important day for Katya. She has a test in a programming class. As always, she was given an interesting problem that she solved very fast. Can you solve that problem?

You are given $n$ ordered segments sets. Each segment can be represented as a pair of two integers $[l, r]$ where $l\leq r$. Each set can contain an arbitrary number of segments (even $0$). It is possible that some segments are equal.

You are also given $m$ queries, each of them can be represented as four numbers: $a, b, x, y$. For each segment, find out whether it is true that each set $p$ ($a\leq p\leq b$) contains at least one segment $[l, r]$ that lies entirely on the segment $[x, y]$, that is $x\leq l\leq r\leq y$.

Find out the answer to each query.

Note that you need to solve this problem online. That is, you will get a new query only after you print the answer for the previous query.

Input

The first line contains three integers $n$, $m$, and $k$ $(1\leq n,m\leq 10^5, 1\leq k\leq 3\cdot10^5)$ — the number of sets, queries, and segments respectively.

Each of the next $k$ lines contains three integers $l$, $r$, and $p$ $(1\leq l\leq r\leq 10^9, 1\leq p\leq n)$ — the limits of the segment and the index of a set, to which this segment belongs.

Each of the next $m$ lines contains four integers $a, b, x, y$ $(1\leq a\leq b\leq n, 1\leq x\leq y\leq 10^9)$ — the description of the query.

Output

For each query, print "yes" or "no" in a new line.

Interaction

After printing a query, do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

* fflush(stdout) or cout.flush() in C++;
* System.out.flush() in Java;
* flush(output) in Pascal;
* stdout.flush() in Python;
* see documentation for other languages.

Example

Input

```
5 5 9
3 6 3
1 3 1
2 4 2
1 2 3
4 6 5
2 5 3
7 9 4
2 3 1
4 10 4
1 2 2 3
1 2 2 4
1 3 1 5
2 3 3 6
2 4 2 9
```

Output

```
no
yes
yes
no
yes
```

Note

For the first query, the answer is negative since the second set does not contain a segment that lies on the segment $[2, 3]$.

In the second query, the first set contains $[2, 3]$, and the second set contains $[2, 4]$.

In the third query, the first set contains $[2, 3]$, the second set contains $[2, 4]$, and the third set contains $[2, 5]$.

In the fourth query, the second set does not contain a segment that lies on the segment $[3, 6]$.

In the fifth query, the second set contains $[2, 4]$, the third set contains $[2, 5]$, and the fourth contains $[7, 9]$.

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << '\n';
        tout << "query_limit=" << query_limit << '\n';
        tout.flush();
    } catch (...) {
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

static string toLowerStr(string s) {
    for (char &c : s) c = (char)tolower((unsigned char)c);
    return s;
}

struct Seg {
    long long l, r;
    int p; 
};

struct Query {
    int a, b; 
    long long x, y;
    int idx;
};

struct SegTreeMax {
    int n = 0;
    int sz = 0;
    vector<long long> t;

    static constexpr long long NEG_INF = (long long)-4e18;

    explicit SegTreeMax(int n_) { init(n_); }

    void init(int n_) {
        n = n_;
        sz = 1;
        while (sz < n) sz <<= 1;
        t.assign(2 * sz, (long long)4e18);
    }

    void setPoint(int pos, long long val) {
        int i = pos + sz;
        t[i] = val;
        for (i >>= 1; i >= 1; i >>= 1) {
            t[i] = max(t[i << 1], t[i << 1 | 1]);
        }
    }

    long long rangeMax(int l, int r) const {
        long long res = NEG_INF;
        int L = l + sz, R = r + sz;
        while (L <= R) {
            if (L & 1) res = max(res, t[L++]);
            if (!(R & 1)) res = max(res, t[R--]);
            L >>= 1;
            R >>= 1;
        }
        return res;
    }
};

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    try {
        int n = inf.readInt(1, 100000, "n");
        int m = inf.readInt(1, 100000, "m");
        int k = inf.readInt(1, 300000, "k");

        vector<Seg> segs;
        segs.reserve(k);
        vector<tuple<long long,long long,int>> segs_in_order;
        segs_in_order.reserve(k);

        for (int i = 0; i < k; i++) {
            long long l = inf.readLong(1, 1000000000LL, "l");
            long long r = inf.readLong(l, 1000000000LL, "r");
            int p = inf.readInt(1, n, "p");
            segs.push_back({l, r, p - 1});
            segs_in_order.emplace_back(l, r, p);
        }

        vector<Query> qs;
        qs.reserve(m);
        vector<array<long long,4>> qs_in_order;
        qs_in_order.reserve(m);
        for (int i = 0; i < m; i++) {
            int a = inf.readInt(1, n, "a");
            int b = inf.readInt(a, n, "b");
            long long x = inf.readLong(1, 1000000000LL, "x");
            long long y = inf.readLong(x, 1000000000LL, "y");
            qs.push_back({a - 1, b - 1, x, y, i});
            qs_in_order.push_back({(long long)a, (long long)b, x, y});
        }

        
        query_limit = m;
        long long hard_cap = max(200000LL, 10LL * query_limit);

        
        sort(segs.begin(), segs.end(), [](const Seg& A, const Seg& B) {
            if (A.l != B.l) return A.l > B.l;
            if (A.r != B.r) return A.r < B.r;
            return A.p < B.p;
        });
        vector<Query> qs_sorted = qs;
        sort(qs_sorted.begin(), qs_sorted.end(), [](const Query& A, const Query& B) {
            if (A.x != B.x) return A.x > B.x;
            return A.idx < B.idx;
        });

        const long long INF = (long long)4e18;
        vector<long long> best_r(n, INF);
        SegTreeMax st(n);
        for (int i = 0; i < n; i++) st.setPoint(i, best_r[i]);

        vector<char> expected(m, 0);
        int si = 0;
        for (const auto& q : qs_sorted) {
            while (si < (int)segs.size() && segs[si].l >= q.x) {
                int p = segs[si].p;
                long long r = segs[si].r;
                if (r < best_r[p]) {
                    best_r[p] = r;
                    st.setPoint(p, best_r[p]);
                }
                si++;
            }
            long long mx = st.rangeMax(q.a, q.b);
            expected[q.idx] = (mx <= q.y) ? 1 : 0;
        }

        
        log_metrics();

        
        cout << n << " " << m << " " << k << endl;
        for (auto &t : segs_in_order) {
            long long l, r;
            int p;
            tie(l, r, p) = t;
            cout << l << " " << r << " " << p << endl;
        }

        
        for (int i = 0; i < m; i++) {
            auto q = qs_in_order[i];
            cout << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << endl;

            string ans;
            if (!(cin >> ans)) {
                finish(_pe, "Unexpected EOF from contestant while waiting for answer");
            }
            queries++;
            if (queries > hard_cap) {
                finish(_pe, "Hard cap exceeded (possible infinite output)");
            }

            ans = toLowerStr(ans);
            bool got;
            if (ans == "yes") got = true;
            else if (ans == "no") got = false;
            else finish(_pe, "Invalid answer token (expected yes/no)");

            bool exp = (expected[i] != 0);
            if (got != exp) {
                finish(_wa, string("Wrong answer on query #") + to_string(i + 1));
            }

            
            if ((i & 4095) == 4095) log_metrics();
        }

        finish(_ok, "OK");
    } catch (const std::exception& e) {
        finish(_pe, string("Interactor exception: ") + e.what());
    } catch (...) {
        finish(_pe, "Interactor unknown exception");
    }
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct Seg {
    long long l, r;
    int p;
};

struct Query {
    int a, b;
    long long x, y;
    int idx;
};

static long long clampLL(long long v, long long lo, long long hi) {
    if (v < lo) return lo;
    if (v > hi) return hi;
    return v;
}

struct SegTreeMax {
    int n = 0;
    int base = 1;
    vector<long long> t;

    void init(int n_) {
        n = n_;
        base = 1;
        while (base < n) base <<= 1;
        t.assign(base * 2, 0LL);
    }

    void setVal(int pos0, long long val) {
        int i = pos0 + base;
        t[i] = val;
        for (i >>= 1; i >= 1; i >>= 1) {
            t[i] = max(t[i << 1], t[(i << 1) | 1]);
            if (i == 1) break;
        }
    }

    long long queryMax(int l0, int r0) const {
        int l = l0 + base;
        int r = r0 + base;
        long long res = 0;
        while (l <= r) {
            if (l & 1) res = max(res, t[l++]);
            if (!(r & 1)) res = max(res, t[r--]);
            l >>= 1;
            r >>= 1;
        }
        return res;
    }
};

static vector<Query> generateQueries(int n, int m, int k, const vector<Seg>& segs, const vector<int>& emptySets) {
    const long long LIM = 1000000000LL;
    vector<Query> qs;
    qs.reserve(m);

    auto makeRangeCentered = [&](int center, int len) -> pair<int,int> {
        len = max(1, min(n, len));
        int a = center - len / 2;
        if (a < 1) a = 1;
        if (a + len - 1 > n) a = n - len + 1;
        if (a < 1) a = 1;
        int b = a + len - 1;
        return {a, b};
    };

    for (int i = 0; i < m; i++) {
        int a = 1, b = n;
        long long x = 1, y = LIM;

        if (i == 0) {
            a = 1; b = n; x = 1; y = LIM;
        } else if (i == 1) {
            a = 1; b = 1; x = 1; y = 1;
        } else if (!emptySets.empty() && (i % 37 == 0)) {
            int e = emptySets[(i / 37) % (int)emptySets.size()];
            int len = 1 + (i % min(n, 100));
            auto ab = makeRangeCentered(e, len);
            a = ab.first; b = ab.second;
            x = 1; y = LIM;
        } else {
            long long idx = ( (long long)i * 1000003LL + 12345LL ) % (long long)k;
            const Seg& s = segs[(int)idx];

            int len;
            switch (i % 5) {
                case 0: len = 1; break;
                case 1: len = min(n, 2); break;
                case 2: len = min(n, 31); break;
                case 3: len = min(n, 500); break;
                default: len = n; break;
            }
            auto ab = makeRangeCentered(s.p, len);
            a = ab.first; b = ab.second;

            long long l = s.l, r = s.r;
            switch (i % 6) {
                case 0:
                    x = l; y = r;
                    break;
                case 1: {
                    long long d = i % 7;
                    x = clampLL(l - d, 1, LIM);
                    y = clampLL(r + d, 1, LIM);
                    if (x > y) swap(x, y);
                    break;
                }
                case 2: {
                    long long d = i % 11;
                    x = clampLL(l + d, 1, LIM);
                    y = clampLL(r, 1, LIM);
                    if (x > y) x = y;
                    break;
                }
                case 3: {
                    x = clampLL(l, 1, LIM);
                    long long mid = l + (r - l) / 2;
                    y = clampLL(mid, 1, LIM);
                    if (x > y) swap(x, y);
                    break;
                }
                case 4:
                    x = 1;
                    y = clampLL(r, 1, LIM);
                    if (x > y) x = y;
                    break;
                default:
                    x = clampLL(l, 1, LIM);
                    y = LIM;
                    if (x > y) x = y;
                    break;
            }
        }

        x = clampLL(x, 1, LIM);
        y = clampLL(y, 1, LIM);
        if (x > y) swap(x, y);
        qs.push_back({a, b, x, y, i});
    }
    return qs;
}

static vector<char> computeAnswers(int n, const vector<Seg>& segments, const vector<Query>& queries) {
    const long long INF = (long long)4e18;

    vector<Seg> segs = segments;
    sort(segs.begin(), segs.end(), [&](const Seg& A, const Seg& B) {
        if (A.l != B.l) return A.l > B.l;
        if (A.r != B.r) return A.r < B.r;
        return A.p < B.p;
    });

    vector<Query> qs = queries;
    sort(qs.begin(), qs.end(), [&](const Query& A, const Query& B) {
        if (A.x != B.x) return A.x > B.x;
        return A.idx < B.idx;
    });

    vector<long long> minR(n + 1, INF);
    SegTreeMax st;
    st.init(n);
    for (int p = 1; p <= n; p++) st.setVal(p - 1, minR[p]);

    vector<char> ans(queries.size(), 0);
    size_t ptr = 0;
    for (const auto& q : qs) {
        while (ptr < segs.size() && segs[ptr].l >= q.x) {
            const Seg& s = segs[ptr++];
            long long nv = min(minR[s.p], s.r);
            if (nv != minR[s.p]) {
                minR[s.p] = nv;
                st.setVal(s.p - 1, nv);
            }
        }
        long long mx = st.queryMax(q.a - 1, q.b - 1);
        ans[q.idx] = (mx <= q.y) ? 1 : 0;
    }
    return ans;
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n = inf.readInt(1, 100000, "n");
    int m = inf.readInt(1, 100000, "m");
    int k = inf.readInt(1, 300000, "k");

    query_limit = m;
    queries = 0;
    log_metrics(); 

    vector<int> cnt(n + 1, 0);
    vector<Seg> segments;
    segments.reserve(k);

    for (int i = 0; i < k; i++) {
        long long l = inf.readLong(1, 1000000000LL, "l");
        long long r = inf.readLong(l, 1000000000LL, "r");
        int p = inf.readInt(1, n, "p");
        segments.push_back({l, r, p});
        cnt[p]++;
    }

    vector<int> emptySets;
    emptySets.reserve(n);
    for (int p = 1; p <= n; p++) if (cnt[p] == 0) emptySets.push_back(p);

    vector<Query> qs = generateQueries(n, m, k, segments, emptySets);
    vector<char> expected = computeAnswers(n, segments, qs);

    
    cout << n << " " << m << " " << k << "\n";
    for (const auto& s : segments) {
        cout << s.l << " " << s.r << " " << s.p << "\n";
    }
    cout.flush();

    long long hard_cap = max(200000LL, 10LL * query_limit);

    for (int i = 0; i < m; i++) {
        const auto& q = qs[i];

        queries++;
        if (queries > hard_cap) finish(_pe, "too many queries (hard cap exceeded)");

        cout << q.a << " " << q.b << " " << q.x << " " << q.y << endl; 

        string token;
        if (!(cin >> token)) finish(_pe, "unexpected EOF from contestant");

        for (char& c : token) c = (char)tolower((unsigned char)c);
        bool got;
        if (token == "yes") got = true;
        else if (token == "no") got = false;
        else finish(_pe, "invalid answer token (expected yes/no)");

        bool exp = (expected[i] != 0);
        if (got != exp) {
            finish(_wa, "wrong answer");
        }
    }

    finish(_ok, "ok");
    return 0;
}


Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

struct Seg {
    long long l, r;
};

static long long clampLL(long long v, long long lo, long long hi) {
    if (v < lo) return lo;
    if (v > hi) return hi;
    return v;
}

int main(int argc, char** argv) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    int seed = 1;
    if (argc >= 2) seed = atoi(argv[1]);

    auto outputCase = [&](int n, int m, int k, const vector<tuple<long long,long long,int>>& segments,
                          const vector<array<long long,4>>& queries) {
        cout << n << " " << m << " " << k << "\n";
        for (auto &e : segments) {
            long long l, r;
            int p;
            tie(l, r, p) = e;
            cout << l << " " << r << " " << p << "\n";
        }
        if (mode == "non") {
            for (auto &q : queries) {
                cout << q[0] << " " << q[1] << " " << q[2] << " " << q[3] << "\n";
            }
        } else if (mode == "adp") {
            
            
        } else {
            quitf(_fail, "Unknown --mode='%s' (expected 'non' or 'adp')", mode.c_str());
        }
    };

    if (seed == 1) {
        int n = 1, m = 1, k = 1;
        vector<tuple<long long,long long,int>> segments;
        segments.emplace_back(1, 1, 1);
        vector<array<long long,4>> queries;
        queries.push_back({1, 1, 1, 1});
        outputCase(n, m, k, segments, queries);
        return 0;
    }
    if (seed == 2) {
        int n = 2, m = 4, k = 2;
        vector<tuple<long long,long long,int>> segments;
        segments.emplace_back(1, 2, 1);
        segments.emplace_back(1, 2, 1); 
        vector<array<long long,4>> queries;
        queries.push_back({1, 1, 1, 2}); 
        queries.push_back({2, 2, 1, 2}); 
        queries.push_back({1, 2, 1, 2}); 
        queries.push_back({1, 2, 2, 2}); 
        outputCase(n, m, k, segments, queries);
        return 0;
    }
    if (seed == 3) {
        int n = 3, m = 6, k = 5;
        vector<tuple<long long,long long,int>> segments;
        segments.emplace_back(1, 1, 1);
        segments.emplace_back(2, 3, 1);
        segments.emplace_back(2, 2, 2);
        segments.emplace_back(1000000000LL, 1000000000LL, 2);
        segments.emplace_back(5, 10, 3);
        vector<array<long long,4>> queries;
        queries.push_back({1, 2, 2, 2});
        queries.push_back({1, 1, 1, 1});
        queries.push_back({2, 2, 1000000000LL, 1000000000LL});
        queries.push_back({2, 3, 2, 10});
        queries.push_back({1, 3, 3, 3});
        queries.push_back({3, 3, 1, 4});
        outputCase(n, m, k, segments, queries);
        return 0;
    }

    int n = opt<int>("n", 100000);
    int m = opt<int>("m", 100000);
    int k = opt<int>("k", 300000);

    n = max(1, min(100000, n));
    m = max(1, min(100000, m));
    k = max(1, min(300000, k));

    vector<int> cnt(n + 1, 0);
    int remaining = k;
    if (k >= n) {
        for (int p = 1; p <= n; p++) cnt[p] = 1;
        remaining -= n;
    } else {
        for (int p = 1; p <= k; p++) cnt[p] = 1;
        remaining = 0;
    }

    while (remaining > 0) {
        int p = rnd.next(1, n);
        int add;
        int coin = rnd.next(0, 99);
        if (coin < 8) add = rnd.next(500, 2000);
        else if (coin < 35) add = rnd.next(50, 350);
        else add = rnd.next(1, 25);
        add = min(add, remaining);
        cnt[p] += add;
        remaining -= add;
    }

    vector<vector<Seg>> segs(n + 1);
    segs.shrink_to_fit();

    auto pickNonEmptySetInRange = [&](int a, int b) -> int {
        if (a > b) return a;
        for (int tries = 0; tries < 10; tries++) {
            int p = rnd.next(a, b);
            if (!segs[p].empty()) return p;
        }
        for (int p = a; p <= b; p++) if (!segs[p].empty()) return p;
        for (int p = 1; p <= n; p++) if (!segs[p].empty()) return p;
        return 1;
    };

    const long long LIM = 1000000000LL;

    vector<tuple<long long,long long,int>> allSegments;
    allSegments.reserve(k);

    int generated = 0;
    for (int p = 1; p <= n; p++) {
        int c = cnt[p];
        if (c <= 0) continue;

        int stepL = rnd.next(1, 3);
        int d = rnd.next(stepL + 1, 1000);

        long long extra;
        int hardType = (p % 10 == 0) ? 1 : 0;
        if (hardType) extra = rnd.next(100000000LL, 300000000LL);
        else extra = rnd.next(0LL, 20000LL);

        long long span = (long long)(c - 1) * (long long)(stepL + d) + extra + 5LL;
        if (span >= LIM) {
            extra = min(extra, 20000LL);
            span = (long long)(c - 1) * (long long)(stepL + d) + extra + 5LL;
        }
        if (span >= LIM) {
            stepL = 1;
            d = 2;
            extra = 0;
            span = (long long)(c - 1) * (long long)(stepL + d) + extra + 5LL;
        }

        long long maxBase = LIM - span;
        if (maxBase < 1) maxBase = 1;
        long long base = rnd.next(1LL, maxBase);

        segs[p].reserve(c);
        for (int j = 0; j < c; j++) {
            long long l = base + (long long)j * (long long)stepL;
            long long r = l + extra + (long long)(c - 1 - j) * (long long)d;
            if (r > LIM) r = LIM;
            if (l > r) l = r;
            segs[p].push_back({l, r});
            allSegments.emplace_back(l, r, p);
            generated++;
        }
    }

    if (generated != k) {
        if (generated > k) {
            allSegments.resize(k);
            for (int p = 1; p <= n; p++) segs[p].clear();
            for (auto &t : allSegments) {
                long long l, r;
                int p;
                tie(l, r, p) = t;
                segs[p].push_back({l, r});
            }
            generated = k;
        } else {
            while (generated < k) {
                int p = rnd.next(1, n);
                long long l = rnd.next(1LL, LIM);
                long long r = rnd.next(l, LIM);
                segs[p].push_back({l, r});
                allSegments.emplace_back(l, r, p);
                generated++;
            }
        }
    }

    vector<array<long long,4>> queries;
    queries.reserve(m);

    for (int qi = 0; qi < m; qi++) {
        int a, b;
        if (n == 1) {
            a = b = 1;
        } else {
            int t = rnd.next(0, 99);
            int len;
            if (t < 45) len = rnd.next(max(1, n / 2), n);
            else if (t < 80) len = rnd.next(1, min(n, 2000));
            else len = rnd.next(1, min(n, 60));
            a = rnd.next(1, n - len + 1);
            b = a + len - 1;
        }

        int qset = pickNonEmptySetInRange(a, b);
        int idx = (int)rnd.next(0, (int)segs[qset].size() - 1);
        long long l = segs[qset][idx].l;
        long long r = segs[qset][idx].r;

        long long x, y;
        int pat = rnd.next(0, 99);
        if (pat < 10) {
            x = 1;
            y = LIM;
        } else if (pat < 45) {
            x = clampLL(l - rnd.next(0LL, 5LL), 1LL, LIM);
            y = clampLL(r + rnd.next(0LL, 5LL), x, LIM);
        } else if (pat < 70) {
            x = clampLL(l + rnd.next(0LL, 7LL), 1LL, LIM);
            y = clampLL(r - rnd.next(0LL, 5LL), x, LIM);
        } else if (pat < 85) {
            x = clampLL(l - rnd.next(0LL, 50LL), 1LL, LIM);
            y = clampLL(r - rnd.next(0LL, 1LL), x, LIM);
        } else {
            x = rnd.next(1LL, LIM);
            long long width = rnd.next(0LL, 200000LL);
            y = x + width;
            if (y > LIM) y = LIM;
        }

        if (x > y) swap(x, y);
        queries.push_back({(long long)a, (long long)b, x, y});
    }

    outputCase(n, m, k, allSegments, queries);
    return 0;
}