Problem: cf750_h
Difficulty: hard
Categories: graph, data structures
Time limit: 9000 ms, memory limit: 256 MB

H. New Year and Snowy Grid

Pay attention to the output section below, where you will see the information about flushing the output.

Bearland is a grid with *h* rows and *w* columns. Rows are numbered 1 through *h* from top to bottom. Columns are numbered 1 through *w* from left to right. Every cell is either allowed (denoted by '.' in the input) or permanently blocked (denoted by '#').

Bearland is a cold land, where heavy snow often makes travelling harder. Every day a few allowed cells are temporarily blocked by snow. Note, that this block works only on this particular day and next day any of these cells might be allowed again (unless there is another temporarily block).

It's possible to move directly between two cells only if they share a side and none of them is permanently or temporarily blocked.

Limak is a little polar bear who lives in Bearland. His house is at the top left cell, while his school is at the bottom right cell. Every day Limak should first go from his house to the school and then return back to his house. Since he gets bored easily, he doesn't want to visit the same cell twice on one day, except for the cell with his house, where he starts and ends. If Limak can reach a school and return home avoiding revisiting cells, he calls a day interesting.

There are *q* days you must process, one after another. For each of these days you should check if it's interesting and print "YES" or "NO" on a separate line. In order to be able to read the description of the next day you should print the answer for the previous one and flush the output.

It's guaranteed that a day with no cells temporarily blocked by snow would be interesting. It's also guaranteed that cells with Limak's house and school are never blocked (neither permanently or temporarily).

Input

The first line of the input contains three integers *h*, *w* and *q* (2 ≤ *h*, *w* ≤ 1000, 1 ≤ *q* ≤ 10 000) — the height and the width of the grid, and the number of days, respectively.

Next *h* lines describe which cells are allowed and which permanently blocked. The *i*-th line contains a string of length *w*, describing the *i*-th row. Every character is either '.' (denoting an allowed cell) or '#' (denoting a permanently blocked cell). It's guaranteed that a day with no cells temporarily blocked by snow would be interesting.

Then, the description of *q* days is given. The description of the *i*-th day starts with a line containing a single integer *k**i* (1 ≤ *k**i* ≤ 10) — the number of cells that are temporarily blocked by snow on that day. Each of next *k**i* lines contains two integers *r**i*, *j* and *c**i*, *j* (1 ≤ *r**i*, *j* ≤ *h*, 1 ≤ *c**i*, *j* ≤ *w*), representing a cell at the intersection of the row *r**i*, *j* and the column *c**i*, *j*. The given *k**i* cells are distinct and none of them is permanently blocked. Also, none of them contains Limak's house or school.

Output

For each of *q* days print "YES" if that day is interesting, and otherwise print "NO", both without the quotes. After printing an answer, you have to both print the end-of-line character and flush the output. Then you can proceed to the next day. You can get Idleness Limit Exceeded if you don't print anything or if you forget to flush the output.

To flush you can use (just after printing a YES/NO and end-of-line):

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

Examples

Input

```
3 5 4  
.....  
.....  
.#...  
1  
1 4  
1  
1 5  
2  
2 4  
3 1  
2  
1 5  
3 3
```

Output

```
NO  
YES  
YES  
NO
```

Input

```
9 31 5  
...............................  
...............................  
.###.###.#.###...###.###.#.###.  
...#.#.#.#.#.......#.#.#.#...#.  
.###.#.#.#.###...###.#.#.#...#.  
.#...#.#.#.#.#...#...#.#.#...#.  
.###.###.#.###...###.###.#...#.  
...............................  
...............................  
5  
6 5  
2 11  
1 14  
8 15  
2 14  
5  
2 14  
1 14  
8 16  
6 5  
2 11  
3  
2 2  
1 4  
8 30  
10  
3 1  
3 11  
5 16  
7 21  
4 16  
3 5  
7 31  
3 9  
7 25  
3 27  
10  
3 1  
3 9  
7 25  
3 27  
7 21  
4 17  
3 5  
7 31  
4 16  
3 11
```

Output

```
NO  
YES  
YES  
YES  
NO
```

Note

In the first sample, there are 4 days. Drawings below show how Limak could go to school and return to his home in the second and the third day (on the left and on the right respectively). A permanently blocked cell is painted red, while cells temporarily blocked by snow are painted orange. Black and green arrows should Limak's way to the school and back to the house respectively.

 ![](https://espresso.codeforces.com/6881ec4c3b5047da77cee145ab8b435b898cda94.png) 

For the second sample, below you can see how the grid looks like on each day, where '#' denotes a cell that is blocked, either temporarily or permanently.

 ![](https://espresso.codeforces.com/1a1f37c66eb3f754847a5159910610380404618d.png)

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 << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
    }
}

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

static string to_upper(string s) {
    for (char& ch : s) ch = (char)toupper((unsigned char)ch);
    return s;
}

static bool parse_yes_no(const string& token, bool& value_out) {
    string s = to_upper(token);
    if (s == "YES") {
        value_out = true;
        return true;
    }
    if (s == "NO") {
        value_out = false;
        return true;
    }
    return false;
}

struct DualityChecker {
    int H = 0;
    int W = 0;
    int H2 = 0;
    int W2 = 0;

    vector<int> cc;  
    int base_cc_cnt = 0;
    int side1 = 0;
    int side2 = 0;

    unordered_set<uint64_t> edges;  

    vector<int> parent;
    vector<int> sz;
    vector<int> touched;

    explicit DualityChecker(int h, int w, const vector<string>& grid) : H(h), W(w) {
        H2 = H + 2;
        W2 = W + 2;

        auto idx = [&](int r, int c) -> int { return r * W2 + c; };
        auto in_range = [&](int r, int c) -> bool { return 0 <= r && r <= H + 1 && 0 <= c && c <= W + 1; };

        vector<unsigned char> allow(H2 * W2, 0);
        cc.assign(H2 * W2, 0);

        for (int r = 1; r <= H; r++) {
            for (int c = 1; c <= W; c++) {
                allow[idx(r, c)] = (grid[r - 1][c - 1] == '#') ? 1 : 0;
            }
        }

        for (int r = 3; r <= H + 1; r++) allow[idx(r, 0)] = 1;
        for (int c = 1; c <= W - 2; c++) allow[idx(H + 1, c)] = 1;
        for (int c = 3; c <= W + 1; c++) allow[idx(0, c)] = 1;
        for (int r = 1; r <= H - 2; r++) allow[idx(r, W + 1)] = 1;

        vector<int> st;
        st.reserve((H + 2) * (W + 2));

        for (int r = 0; r <= H + 1; r++) {
            for (int c = 0; c <= W + 1; c++) {
                int pos = idx(r, c);
                if (!allow[pos] || cc[pos] != 0) continue;
                ++base_cc_cnt;
                cc[pos] = base_cc_cnt;
                st.push_back(pos);
                while (!st.empty()) {
                    int cur = st.back();
                    st.pop_back();
                    int cr = cur / W2;
                    int cc0 = cur % W2;
                    for (int dr = -1; dr <= 1; dr++) {
                        for (int dc = -1; dc <= 1; dc++) {
                            int nr = cr + dr;
                            int nc = cc0 + dc;
                            if (!in_range(nr, nc)) continue;
                            int np = idx(nr, nc);
                            if (!allow[np] || cc[np] != 0) continue;
                            cc[np] = base_cc_cnt;
                            st.push_back(np);
                        }
                    }
                }
            }
        }

        side1 = cc[idx(3, 0)];
        side2 = cc[idx(0, 3)];
        if (side1 == 0 || side2 == 0) {
            finish(_fail, "Invalid case: missing SIDE1/SIDE2 components");
        }

        parent.assign(base_cc_cnt + 11, 0);
        sz.assign(base_cc_cnt + 11, 0);

        long long allow_cells = 0;
        for (int v : cc) allow_cells += (v != 0);
        edges.reserve((size_t)min(1LL << 22, max(1LL, allow_cells * 2)));

        for (int r = 0; r <= H + 1; r++) {
            for (int c = 0; c <= W + 1; c++) {
                int a = cc[idx(r, c)];
                if (a == 0) continue;
                for (int dr = 0; dr <= 2; dr++) {
                    for (int dc = -2; dc <= 2; dc++) {
                        if (dr == 0 && dc <= 0) continue;
                        int nr = r + dr;
                        int nc = c + dc;
                        if (!in_range(nr, nc)) continue;
                        int b = cc[idx(nr, nc)];
                        if (b == 0 || b == a) continue;
                        int x = min(a, b);
                        int y = max(a, b);
                        edges.insert((uint64_t)((uint32_t)x) << 32 | (uint32_t)y);
                    }
                }
            }
        }
    }

    void dsu_clear() {
        for (int x : touched) {
            parent[x] = 0;
            sz[x] = 0;
        }
        touched.clear();
    }

    void dsu_init(int x) {
        if (x <= 0 || x >= (int)parent.size()) {
            finish(_fail, "DSU index out of range");
        }
        if (parent[x] != 0) return;
        parent[x] = x;
        sz[x] = 1;
        touched.push_back(x);
    }

    int dsu_find(int x) {
        dsu_init(x);
        while (parent[x] != x) {
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    }

    void dsu_union(int a, int b) {
        a = dsu_find(a);
        b = dsu_find(b);
        if (a == b) return;
        if (sz[a] < sz[b]) swap(a, b);
        parent[b] = a;
        sz[a] += sz[b];
    }

    bool duality(const vector<pair<int, int>>& snow) {
        auto idx = [&](int r, int c) -> int { return r * W2 + c; };
        auto in_range = [&](int r, int c) -> bool { return 0 <= r && r <= H + 1 && 0 <= c && c <= W + 1; };

        dsu_clear();
        dsu_init(side1);
        dsu_init(side2);

        const int next_id = base_cc_cnt + 1;
        vector<int> snow_pos;
        snow_pos.reserve(snow.size());

        for (int i = 0; i < (int)snow.size(); i++) {
            int r = snow[i].first;
            int c = snow[i].second;
            if (r < 1 || r > H || c < 1 || c > W) {
                finish(_fail, "Snow cell out of bounds");
            }
            if ((r == 1 && c == 1) || (r == H && c == W)) {
                finish(_fail, "Snow cell hits start/end");
            }
            int pos = idx(r, c);
            if (cc[pos] != 0) {
                finish(_fail, "Snow cell is permanently blocked or duplicated");
            }
            int id = next_id + i;
            cc[pos] = id;
            snow_pos.push_back(pos);
        }

        for (int i = 0; i < (int)snow.size(); i++) {
            int r = snow[i].first;
            int c = snow[i].second;
            int id = next_id + i;
            dsu_init(id);
            for (int dr = -1; dr <= 1; dr++) {
                for (int dc = -1; dc <= 1; dc++) {
                    int nr = r + dr;
                    int nc = c + dc;
                    if (!in_range(nr, nc)) continue;
                    int b = cc[idx(nr, nc)];
                    if (b != 0) dsu_union(id, b);
                }
            }
        }

        int root1 = dsu_find(side1);
        int root2 = dsu_find(side2);
        bool ok = false;

        if (root1 == root2) {
            ok = true;
        } else {
            vector<int> g1;
            vector<int> g2;
            g1.reserve(touched.size());
            g2.reserve(touched.size());
            for (int x : touched) {
                int rx = dsu_find(x);
                if (rx == root1) g1.push_back(x);
                else if (rx == root2) g2.push_back(x);
            }
            for (int x : g1) {
                if (x > base_cc_cnt) continue;
                for (int y : g2) {
                    if (y > base_cc_cnt) continue;
                    int a = min(x, y);
                    int b = max(x, y);
                    uint64_t key = (uint64_t)((uint32_t)a) << 32 | (uint32_t)b;
                    if (edges.find(key) != edges.end()) {
                        ok = true;
                        break;
                    }
                }
                if (ok) break;
            }
        }

        if (!ok) {
            for (int i = 0; i < (int)snow.size(); i++) {
                int r = snow[i].first;
                int c = snow[i].second;
                int id = next_id + i;
                int rid = dsu_find(id);
                for (int dr = -2; dr <= 2; dr++) {
                    for (int dc = -2; dc <= 2; dc++) {
                        int nr = r + dr;
                        int nc = c + dc;
                        if (!in_range(nr, nc)) continue;
                        int b = cc[idx(nr, nc)];
                        if (b == 0) continue;
                        int rb = dsu_find(b);
                        if ((rid == root1 && rb == root2) || (rid == root2 && rb == root1)) {
                            ok = true;
                            break;
                        }
                    }
                    if (ok) break;
                }
                if (ok) break;
            }
        }

        for (int pos : snow_pos) cc[pos] = 0;
        return ok;
    }

    bool expected_yes(const vector<pair<int, int>>& snow) {
        return !duality(snow);
    }
};

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

    int H = inf.readInt(2, 1000, "h");
    int W = inf.readInt(2, 1000, "w");
    int Q = inf.readInt(1, 10000, "q");

    vector<string> grid(H);
    for (int r = 0; r < H; r++) {
        grid[r] = inf.readToken();
        if ((int)grid[r].size() != W) finish(_fail, "Grid width mismatch");
        for (char ch : grid[r]) {
            if (ch != '.' && ch != '#') finish(_fail, "Invalid grid character");
        }
    }

    DualityChecker checker(H, W, grid);

    query_limit = Q;
    log_metrics();

    cout << H << " " << W << " " << Q << endl;
    for (int r = 0; r < H; r++) cout << grid[r] << endl;
    cout.flush();

    for (int qi = 0; qi < Q; qi++) {
        int k = inf.readInt(1, 10, "k");
        vector<pair<int, int>> snow(k);
        set<pair<int, int>> seen;
        for (int j = 0; j < k; j++) {
            int r = inf.readInt(1, H, "r");
            int c = inf.readInt(1, W, "c");
            if (!seen.insert({r, c}).second) finish(_fail, "Duplicate cell in query");
            if (grid[r - 1][c - 1] != '.') finish(_fail, "Snow cell on permanently blocked cell");
            if ((r == 1 && c == 1) || (r == H && c == W)) finish(_fail, "Snow cell hits start/end");
            snow[j] = {r, c};
        }

        cout << k << endl;
        for (auto [r, c] : snow) cout << r << " " << c << endl;
        cout.flush();

        string token;
        if (!(cin >> token)) finish(_pe, "Unexpected EOF from contestant");
        bool got_yes = false;
        if (!parse_yes_no(token, got_yes)) finish(_pe, "Expected YES/NO");

        bool exp_yes = checker.expected_yes(snow);
        if (got_yes != exp_yes) finish(_wa, exp_yes ? "Expected YES" : "Expected NO");

        queries++;
        if ((queries & 127) == 0) log_metrics();
    }

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


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 << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
    }
}

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

static string to_upper(string s) {
    for (char& ch : s) ch = (char)toupper((unsigned char)ch);
    return s;
}

static bool parse_yes_no(const string& token, bool& value_out) {
    string s = to_upper(token);
    if (s == "YES") {
        value_out = true;
        return true;
    }
    if (s == "NO") {
        value_out = false;
        return true;
    }
    return false;
}

struct DualityChecker {
    int H = 0;
    int W = 0;
    int H2 = 0;
    int W2 = 0;

    vector<int> cc;  
    int base_cc_cnt = 0;
    int side1 = 0;
    int side2 = 0;

    unordered_set<uint64_t> edges;  

    vector<int> parent;
    vector<int> sz;
    vector<int> touched;

    explicit DualityChecker(int h, int w, const vector<string>& grid) : H(h), W(w) {
        H2 = H + 2;
        W2 = W + 2;

        auto idx = [&](int r, int c) -> int { return r * W2 + c; };
        auto in_range = [&](int r, int c) -> bool { return 0 <= r && r <= H + 1 && 0 <= c && c <= W + 1; };

        vector<unsigned char> allow(H2 * W2, 0);
        cc.assign(H2 * W2, 0);

        for (int r = 1; r <= H; r++) {
            for (int c = 1; c <= W; c++) {
                allow[idx(r, c)] = (grid[r - 1][c - 1] == '#') ? 1 : 0;
            }
        }

        for (int r = 3; r <= H + 1; r++) allow[idx(r, 0)] = 1;
        for (int c = 1; c <= W - 2; c++) allow[idx(H + 1, c)] = 1;
        for (int c = 3; c <= W + 1; c++) allow[idx(0, c)] = 1;
        for (int r = 1; r <= H - 2; r++) allow[idx(r, W + 1)] = 1;

        vector<int> st;
        st.reserve((H + 2) * (W + 2));

        for (int r = 0; r <= H + 1; r++) {
            for (int c = 0; c <= W + 1; c++) {
                int pos = idx(r, c);
                if (!allow[pos] || cc[pos] != 0) continue;
                ++base_cc_cnt;
                cc[pos] = base_cc_cnt;
                st.push_back(pos);
                while (!st.empty()) {
                    int cur = st.back();
                    st.pop_back();
                    int cr = cur / W2;
                    int cc0 = cur % W2;
                    for (int dr = -1; dr <= 1; dr++) {
                        for (int dc = -1; dc <= 1; dc++) {
                            int nr = cr + dr;
                            int nc = cc0 + dc;
                            if (!in_range(nr, nc)) continue;
                            int np = idx(nr, nc);
                            if (!allow[np] || cc[np] != 0) continue;
                            cc[np] = base_cc_cnt;
                            st.push_back(np);
                        }
                    }
                }
            }
        }

        side1 = cc[idx(3, 0)];
        side2 = cc[idx(0, 3)];
        if (side1 == 0 || side2 == 0) {
            finish(_fail, "Invalid case: missing SIDE1/SIDE2 components");
        }

        parent.assign(base_cc_cnt + 11, 0);
        sz.assign(base_cc_cnt + 11, 0);

        long long allow_cells = 0;
        for (int v : cc) allow_cells += (v != 0);
        edges.reserve((size_t)min(1LL << 22, max(1LL, allow_cells * 2)));

        for (int r = 0; r <= H + 1; r++) {
            for (int c = 0; c <= W + 1; c++) {
                int a = cc[idx(r, c)];
                if (a == 0) continue;
                for (int dr = 0; dr <= 2; dr++) {
                    for (int dc = -2; dc <= 2; dc++) {
                        if (dr == 0 && dc <= 0) continue;
                        int nr = r + dr;
                        int nc = c + dc;
                        if (!in_range(nr, nc)) continue;
                        int b = cc[idx(nr, nc)];
                        if (b == 0 || b == a) continue;
                        int x = min(a, b);
                        int y = max(a, b);
                        edges.insert((uint64_t)((uint32_t)x) << 32 | (uint32_t)y);
                    }
                }
            }
        }
    }

    void dsu_clear() {
        for (int x : touched) {
            parent[x] = 0;
            sz[x] = 0;
        }
        touched.clear();
    }

    void dsu_init(int x) {
        if (x <= 0 || x >= (int)parent.size()) {
            finish(_fail, "DSU index out of range");
        }
        if (parent[x] != 0) return;
        parent[x] = x;
        sz[x] = 1;
        touched.push_back(x);
    }

    int dsu_find(int x) {
        dsu_init(x);
        while (parent[x] != x) {
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    }

    void dsu_union(int a, int b) {
        a = dsu_find(a);
        b = dsu_find(b);
        if (a == b) return;
        if (sz[a] < sz[b]) swap(a, b);
        parent[b] = a;
        sz[a] += sz[b];
    }

    bool duality(const vector<pair<int, int>>& snow) {
        auto idx = [&](int r, int c) -> int { return r * W2 + c; };
        auto in_range = [&](int r, int c) -> bool { return 0 <= r && r <= H + 1 && 0 <= c && c <= W + 1; };

        dsu_clear();
        dsu_init(side1);
        dsu_init(side2);

        const int next_id = base_cc_cnt + 1;
        vector<int> snow_pos;
        snow_pos.reserve(snow.size());

        for (int i = 0; i < (int)snow.size(); i++) {
            int r = snow[i].first;
            int c = snow[i].second;
            if (r < 1 || r > H || c < 1 || c > W) {
                finish(_fail, "Snow cell out of bounds");
            }
            if ((r == 1 && c == 1) || (r == H && c == W)) {
                finish(_fail, "Snow cell hits start/end");
            }
            int pos = idx(r, c);
            if (cc[pos] != 0) {
                finish(_fail, "Snow cell is permanently blocked or duplicated");
            }
            int id = next_id + i;
            cc[pos] = id;
            snow_pos.push_back(pos);
        }

        for (int i = 0; i < (int)snow.size(); i++) {
            int r = snow[i].first;
            int c = snow[i].second;
            int id = next_id + i;
            dsu_init(id);
            for (int dr = -1; dr <= 1; dr++) {
                for (int dc = -1; dc <= 1; dc++) {
                    int nr = r + dr;
                    int nc = c + dc;
                    if (!in_range(nr, nc)) continue;
                    int b = cc[idx(nr, nc)];
                    if (b != 0) dsu_union(id, b);
                }
            }
        }

        int root1 = dsu_find(side1);
        int root2 = dsu_find(side2);
        bool ok = false;

        if (root1 == root2) {
            ok = true;
        } else {
            vector<int> g1;
            vector<int> g2;
            g1.reserve(touched.size());
            g2.reserve(touched.size());
            for (int x : touched) {
                int rx = dsu_find(x);
                if (rx == root1) g1.push_back(x);
                else if (rx == root2) g2.push_back(x);
            }
            for (int x : g1) {
                if (x > base_cc_cnt) continue;
                for (int y : g2) {
                    if (y > base_cc_cnt) continue;
                    int a = min(x, y);
                    int b = max(x, y);
                    uint64_t key = (uint64_t)((uint32_t)a) << 32 | (uint32_t)b;
                    if (edges.find(key) != edges.end()) {
                        ok = true;
                        break;
                    }
                }
                if (ok) break;
            }
        }

        if (!ok) {
            for (int i = 0; i < (int)snow.size(); i++) {
                int r = snow[i].first;
                int c = snow[i].second;
                int id = next_id + i;
                int rid = dsu_find(id);
                for (int dr = -2; dr <= 2; dr++) {
                    for (int dc = -2; dc <= 2; dc++) {
                        int nr = r + dr;
                        int nc = c + dc;
                        if (!in_range(nr, nc)) continue;
                        int b = cc[idx(nr, nc)];
                        if (b == 0) continue;
                        int rb = dsu_find(b);
                        if ((rid == root1 && rb == root2) || (rid == root2 && rb == root1)) {
                            ok = true;
                            break;
                        }
                    }
                    if (ok) break;
                }
                if (ok) break;
            }
        }

        for (int pos : snow_pos) cc[pos] = 0;
        return ok;
    }

    bool expected_yes(const vector<pair<int, int>>& snow) {
        return !duality(snow);
    }
};

static uint64_t fnv1a_64(uint64_t seed, const string& s) {
    uint64_t h = seed;
    for (unsigned char ch : s) {
        h ^= (uint64_t)ch;
        h *= 1099511628211ULL;
    }
    return h;
}

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

    int H = inf.readInt(2, 1000, "h");
    int W = inf.readInt(2, 1000, "w");
    int Q = inf.readInt(1, 10000, "q");

    vector<string> grid(H);
    for (int r = 0; r < H; r++) {
        grid[r] = inf.readToken();
        if ((int)grid[r].size() != W) finish(_fail, "Grid width mismatch");
        for (char ch : grid[r]) {
            if (ch != '.' && ch != '#') finish(_fail, "Invalid grid character");
        }
    }

    vector<pair<int, int>> allowed;
    allowed.reserve((size_t)H * (size_t)W);
    for (int r = 1; r <= H; r++) {
        for (int c = 1; c <= W; c++) {
            if ((r == 1 && c == 1) || (r == H && c == W)) continue;
            if (grid[r - 1][c - 1] == '.') allowed.push_back({r, c});
        }
    }
    if (allowed.empty()) finish(_fail, "No allowed cells for queries");

    DualityChecker checker(H, W, grid);

    uint64_t seed = 1469598103934665603ULL;
    seed ^= (uint64_t)H;
    seed *= 1099511628211ULL;
    seed ^= (uint64_t)W;
    seed *= 1099511628211ULL;
    seed ^= (uint64_t)Q;
    seed *= 1099511628211ULL;
    for (const string& row : grid) seed = fnv1a_64(seed, row);
    mt19937_64 rng(seed);

    query_limit = Q;
    log_metrics();

    cout << H << " " << W << " " << Q << endl;
    for (int r = 0; r < H; r++) cout << grid[r] << endl;
    cout.flush();

    for (int qi = 0; qi < Q; qi++) {
        int k_max = min(10, (int)allowed.size());
        uniform_int_distribution<int> kdist(1, k_max);
        int k = kdist(rng);

        vector<int> picked;
        picked.reserve(k);
        while ((int)picked.size() < k) {
            int idx = (int)(rng() % (uint64_t)allowed.size());
            bool ok = true;
            for (int x : picked) {
                if (x == idx) {
                    ok = false;
                    break;
                }
            }
            if (ok) picked.push_back(idx);
        }
        sort(picked.begin(), picked.end());

        vector<pair<int, int>> snow;
        snow.reserve(k);
        for (int idx : picked) snow.push_back(allowed[idx]);

        cout << k << endl;
        for (auto [r, c] : snow) cout << r << " " << c << endl;
        cout.flush();

        string token;
        if (!(cin >> token)) finish(_pe, "Unexpected EOF from contestant");
        bool got_yes = false;
        if (!parse_yes_no(token, got_yes)) finish(_pe, "Expected YES/NO");

        bool exp_yes = checker.expected_yes(snow);
        if (got_yes != exp_yes) finish(_wa, exp_yes ? "Expected YES" : "Expected NO");

        queries++;
        if ((queries & 127) == 0) log_metrics();
    }

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


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




void ensure_valid(int H, int W, vector<string>& grid) {
    
    for(int c = 0; c < W; c++) grid[0][c] = '.';
    for(int r = 0; r < H; r++) grid[r][W-1] = '.';
    
    
    for(int r = 0; r < H; r++) grid[r][0] = '.';
    for(int c = 0; c < W; c++) grid[H-1][c] = '.';
    
    
    grid[0][0] = '.';
    grid[H-1][W-1] = '.';
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    string mode = opt<string>("mode", "non");

    
    int H = opt<int>("h", rnd.next(2, 1000));
    int W = opt<int>("w", rnd.next(2, 1000));
    int Q = opt<int>("q", rnd.next(1, 10000));

    
    long long seed = atoll(argv[1]);
    bool manual = has_opt("h") || has_opt("w") || has_opt("q");
    if (!manual) {
        if (seed == 1) { H = 2; W = 2; Q = 5; } 
        else if (seed == 2) { H = 1000; W = 1000; Q = 5; } 
        else if (seed == 3) { H = 50; W = 50; Q = 10000; } 
    }

    
    vector<string> grid(H, string(W, '.'));
    
    
    
    
    
    int type = rnd.next(0, 2);

    if (type == 0) {
        int density = rnd.next(10, 60);
        for(int r=0; r<H; r++) 
            for(int c=0; c<W; c++) 
                if (rnd.next(100) < density) grid[r][c] = '#';
    } else if (type == 1) {
        
        for(int r=0; r<H; r++) fill(grid[r].begin(), grid[r].end(), '.');
        int barriers = rnd.next(1, 3);
        for(int b=0; b<barriers; b++) {
            int diag = rnd.next(2, H+W-3);
            for(int r=0; r<H; r++) {
                int c = diag - r;
                if (c >= 0 && c < W) grid[r][c] = '#';
            }
        }
    } else {
        
        int density = rnd.next(50, 80);
        for(int r=0; r<H; r++) 
            for(int c=0; c<W; c++) 
                if (rnd.next(100) < density) grid[r][c] = '#';
    }

    
    ensure_valid(H, W, grid);

    
    cout << H << " " << W << " " << Q << "\n";
    for(int r=0; r<H; r++) {
        cout << grid[r] << "\n";
    }

    
    
    if (mode == "adp") {
        return 0;
    }

    
    
    vector<pair<int,int>> allowed;
    for(int r=0; r<H; r++) {
        for(int c=0; c<W; c++) {
            if ((r==0 && c==0) || (r==H-1 && c==W-1)) continue;
            if (grid[r][c] == '#') continue;
            allowed.push_back({r + 1, c + 1}); 
        }
    }

    for(int i=0; i<Q; i++) {
        int k = rnd.next(1, 10);
        if (k > (int)allowed.size()) k = (int)allowed.size();
        
        cout << k << "\n";
        
        
        set<int> picked;
        while((int)picked.size() < k) {
            picked.insert(rnd.next(0, (int)allowed.size() - 1));
        }

        for(int idx : picked) {
            cout << allowed[idx].first << " " << allowed[idx].second << "\n";
        }
    }

    return 0;
}