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

#include "aagafonov_checker.h"
#include "intersects_algo.h"

using namespace std;

typedef long long ll;

struct TestCase {
    int points;
    bool is_max;
    bool is_formula;
};

vector<TestCase> groups = {
    TestCase{0, false, false},  TestCase{13, false, false}, TestCase{4, true, false},
    TestCase{13, false, false}, TestCase{8, true, false},   TestCase{11, true, true},
    TestCase{9, false, false},  TestCase{8, true, false},   TestCase{9, false, true},
    TestCase{8, true, true},    TestCase{9, false, true},   TestCase{8, true, true},
};

constexpr int kMaxQ = 2'000'000;

struct vect {
    int x, y;

    vect() = default;

    vect(int x_, int y_) : x(x_), y(y_) {}

    vect operator-(const vect &other) const {
        return vect(x - other.x, y - other.y);
    }

    vect operator+(const vect &other) const {
        return vect(x + other.x, y + other.y);
    }

    ll operator^(const vect &other) const {
        return (ll)x * (ll)other.y - (ll)y * (ll)other.x;
    }

    ll operator*(const vect &other) const {
        return (ll)x * (ll)other.x + (ll)y * (ll)other.y;
    }
};

int n, group;
vector<vect> v;

void read_input() {
    n = inf.readInt();
    group = inf.readInt();
    v.clear();
    v.resize(n);
    for (int i = 0; i < n; i++) {
        v[i].x = inf.readInt();
        v[i].y = inf.readInt();
    }
}

uint32_t secret1, secret2;

void calculate_secrets() {
    {
        std::mt19937 rndx(239 * n + 932);
        for (int i = 0; i < 12; ++i) {
            rndx();
        }
        secret1 = rndx();
    }
    {
        std::mt19937 rndx(179 * n + 971);
        for (int i = 0; i < 21; ++i) {
            rndx();
        }
        secret2 = rndx();
    }
}

vector<vect> inds_to_points(vector<int> &inds) {
    vector<vect> p;
    for (auto i : inds) {
        p.push_back(v[i]);
    }
    return p;
}

ll oriented_double_area(const vector<vect> &p) {
    ll area = 0;
    for (int i = 0; i < (int)p.size(); i++) {
        area += (p[i] ^ p[(i + 1) % (int)p.size()]);
    }
    return area;
}

void min_rotate(vector<vect> &p) {
    int id = 0;
    for (int i = 0; i < (int)p.size(); i++) {
        if (make_pair(p[i].x, p[i].y) < make_pair(p[id].x, p[id].y)) {
            id = i;
        }
    }
    rotate(p.begin(), p.begin() + id, p.end());
}

vector<vect> convex_hull(vector<vect> pt) {
    min_rotate(pt);
    sort(pt.begin() + 1, pt.end(), [&](const vect &lhs, const vect &rhs) {
        ll cr = (lhs - pt[0]) ^ (rhs - pt[0]);
        if (cr != 0) {
            return cr > 0;
        }
        ll dl = (lhs - pt[0]) * (lhs - pt[0]);
        ll dr = (rhs - pt[0]) * (rhs - pt[0]);
        return dl < dr;
    });
    vector<vect> st;
    for (const auto &p : pt) {
        while (st.size() >= 2 && ((st.back() - st[st.size() - 2]) ^ (p - st[st.size() - 2])) <= 0) {
            st.pop_back();
        }
        st.emplace_back(p);
    }
    return st;
}

bool check_equal(vector<vect> a, vector<vect> b) {
    min_rotate(a);
    min_rotate(b);
    for (int i = 0; i < (int)a.size(); i++) {
        if (a[i].x != b[i].x || a[i].y != b[i].y) {
            return false;
        }
    }
    return true;
}

bool is_convex(vector<int> poly) {
    auto p = inds_to_points(poly);
    auto hull = convex_hull(p);

    if (oriented_double_area(hull) == 0) {
        return false;
    }
    if (hull.size() != p.size()) {
        return false;
    }

    if (check_equal(p, hull)) {
        return true;
    }
    reverse(hull.begin(), hull.end());
    return check_equal(p, hull);
}

bool are_intersecting_interior(const vector<int> &poly1, const vector<int> &poly2) {
    int idx1 = 0;
    for (int i = 0; i < (int)poly1.size(); ++i) {
        if (make_pair(v[poly1[i]].y, v[poly1[i]].x) <
            make_pair(v[poly1[idx1]].y, v[poly1[idx1]].x)) {
            idx1 = i;
        }
    }
    auto side1 = [&](int i) {
        return v[poly1[(i + 1) % (int)poly1.size()]] - v[poly1[i]];
    };
    int idx2 = 0;
    for (int i = 0; i < (int)poly2.size(); ++i) {
        if (make_pair(v[poly2[i]].y, v[poly2[i]].x) >
            make_pair(v[poly2[idx2]].y, v[poly2[idx2]].x)) {
            idx2 = i;
        }
    }
    auto side2 = [&](int i) {
        return v[poly2[i]] - v[poly2[(i + 1) % (int)poly2.size()]];
    };

    auto pt = v[poly1[idx1]] - v[poly2[idx2]];
    int pos1 = 0;
    int pos2 = 0;
    while (pos1 < (int)poly1.size() || pos2 < (int)poly2.size()) {
        int i1 = (idx1 + pos1) % (int)poly1.size();
        int i2 = (idx2 + pos2) % (int)poly2.size();
        auto vec1 = side1(i1);
        auto vec2 = side2(i2);
        auto nxt = pt;
        if (pos1 < (int)poly1.size() && (pos2 == (int)poly2.size() || (vec1 ^ vec2) >= 0)) {
            nxt = nxt + vec1;
            pos1++;
        } else {
            nxt = nxt + vec2;
            pos2++;
        }
        if ((pt ^ nxt) <= 0) {
            return false;
        }
        pt = nxt;
    }

    return true;
}

int main(int argc, char *argv[]) {
    registerTestlibCmd(argc, argv);

    read_input();
    calculate_secrets();

    auto tc = groups[group];

    string verdict = ouf.readToken("OK|WA", "verdict");
    if (verdict != "OK") {
        std::string message = ":";
        while (!ouf.seekEoln()) {
            message += " " + ouf.readToken();
        }
        quit(_wa, message);
    }

    int n_queries = ouf.readInt(0, kMaxQ, "n_queries");
    int n_polygons = ouf.readInt(0, 2 * n, "n_polygons");
    int n_vertices = 0;
    vector<vector<int>> poly;
    poly.reserve(n_polygons);
    vector<int> used(n, 0);
    int cnt_used = 0;
    for (int i = 0; i < n_polygons; ++i) {
        int n_sides = ouf.readInt(3, n, "n_sides");
        vector<int> idx = ouf.readIntegers(n_sides, 0, n - 1, "sides");
        cnt_used++;
        for (int j : idx) {
            if (used[j] == cnt_used) {
                quitf(_wa, "there are two equal indices in polygon %d", i + 1);
            }
            used[j] = cnt_used;
        }
        poly.emplace_back(idx);
        n_vertices += n_sides;
    }

    ll found_secret_1 = ouf.readUnsignedLong(0, numeric_limits<uint32_t>::max(), "secret1");
    ll found_secret_2 = ouf.readUnsignedLong(0, numeric_limits<uint32_t>::max(), "secret2");
    if (found_secret_1 != secret1 || found_secret_2 != secret2) {
        quitf(_wa, "wrong secrets");
    }

    if (n_vertices > 6 * n) {
        quitf(_wa, "too big total number vertices (> 6n), the answer is incorrect");
    }

    for (int i = 0; i < n_polygons; ++i) {
        if (!is_convex(poly[i])) {
            quitf(_wa, "polygon %d is not convex", i + 1);
        }

        if (oriented_double_area(inds_to_points(poly[i])) < 0) {
            reverse(poly[i].begin(), poly[i].end());
        }
        if (oriented_double_area(inds_to_points(poly[i])) <= 0) {
            quitf(_fail, "bug in checker");
        }
    }

    ll total_area = 0;
    for (int i = 0; i < n_polygons; ++i) {
        total_area += oriented_double_area(inds_to_points(poly[i]));
    }

    auto hull_v = convex_hull(v);
    ll ch_area = abs(oriented_double_area(hull_v));
    int ch_size = hull_v.size();

    if (total_area < ch_area) {
        quitf(_wa, "the total area is smaller than the convex hull area");
    }
    if (total_area > ch_area) {
        quitf(_wa, "polygons intersect for sure: total_area = %lld, ch_area = %lld", total_area,
              ch_area);
    }

    {
        vector<ConvexPolygon> polygons(n_polygons);
        for (int i = 0; i < n_polygons; i++) {
            vector<Point> p;
            for (auto id : poly[i]) {
                p.push_back(Point(v[id].x, v[id].y));
            }
            polygons[i] = ConvexPolygon(p, i);
        }
        auto [i, j] = find_intersections(polygons);

        if (i != -1) {
            // double validate
            if (!are_intersecting_interior(poly[i], poly[j])) {
                quitf(_fail, "polygons %d and %d are expected to intersect, but don't", i + 1,
                      j + 1);
            }
            quitf(_wa, "polygons %d and %d intersect", i + 1, j + 1);
        }
    }

    if (n_polygons <= 100) {
        for (int i = 0; i < n_polygons; ++i) {
            for (int j = i + 1; j < n_polygons; ++j) {
                if (are_intersecting_interior(poly[i], poly[j])) {
                    quitf(_fail, "polygons %d and %d intersect: bug in algorithm", i + 1, j + 1);
                }
            }
        }
    }

    {
        vector<Vector> p;
        for (int i = 0; i < n; ++i) {
            p.push_back(Vector(v[i].x, v[i].y, i));
        }
        if (!check(p, poly)) {
            quitf(_fail, "bug in checker");
        }
    }

    int max_polygons = 2 * n - 2 - ch_size;
    string message = to_string(n_queries) + " queries, " + to_string(n_polygons) + " pieces (" +
                     (n_polygons == max_polygons ? "max" : "not max") + ")";

    if (tc.is_max && n_polygons != max_polygons) {
        quit(_wa, message + ", the number of pieces should be maximized");
    }

    if (group == 0) {
        quit(_ok, message);
    }

    if (!tc.is_formula || n_queries <= 20 * n) {
        quitp(tc.points, "ok");
    }

    ld frac = ((ld)(20 * n) / max(n_queries, 20 * n));
    quitp(floor((frac + 1e-6) * tc.points), message);
}
