#include <bits/stdc++.h>
using namespace std;

// Cooperative corridor mapping.
//
// The maze is a spanning tree over an m x m grid of "rooms" (m = (n+1)/2).
// Rooms live at (odd,odd) grid cells and are ALWAYS empty; (even,even) cells
// are ALWAYS walls. The only cells that carry information are the "corridor"
// cells with exactly one even coordinate, one between each pair of adjacent
// rooms. There are exactly 2*m*(m-1) of them = (n^2-1)/2.
//
// Every agent queries a disjoint stripe of the corridor list, base-95 packs its
// answer bits, and ships them to agent 0. Agent 0 reassembles the full corridor
// bitmap, BFS's the room tree from (0,0) to (m-1,m-1), and claims. This queries
// ~half of what solve_fullmap queries (it never touches rooms or walls).

struct Big {
    vector<uint32_t> limb;
    void trim() { while (!limb.empty() && limb.back() == 0) limb.pop_back(); }
    void shl1_or(int bit) {
        uint64_t carry = bit;
        for (uint32_t &x : limb) { uint64_t cur = (uint64_t)x * 2 + carry; x = (uint32_t)cur; carry = cur >> 32; }
        if (carry) limb.push_back((uint32_t)carry);
    }
    void mul_small_add(uint32_t mul, uint32_t add) {
        uint64_t carry = add;
        for (uint32_t &x : limb) { uint64_t cur = (uint64_t)x * mul + carry; x = (uint32_t)cur; carry = cur >> 32; }
        if (carry) limb.push_back((uint32_t)carry);
    }
    uint32_t div_small(uint32_t div) {
        uint64_t rem = 0;
        for (int i = (int)limb.size() - 1; i >= 0; --i) { uint64_t cur = (rem << 32) | limb[i]; limb[i] = (uint32_t)(cur / div); rem = cur % div; }
        trim(); return (uint32_t)rem;
    }
    bool bit(int pos) const { int w = pos / 32, b = pos % 32; return w < (int)limb.size() && ((limb[w] >> b) & 1u); }
    int bit_length() const { if (limb.empty()) return 0; uint32_t high = limb.back(); return ((int)limb.size() - 1) * 32 + (32 - __builtin_clz(high)); }
};

struct Dsu {
    vector<int> p, sz;
    Dsu(int n = 0) : p(n), sz(n, 1) { iota(p.begin(), p.end(), 0); }
    int find(int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }
    bool unite(int a, int b) {
        a = find(a);
        b = find(b);
        if (a == b) return false;
        if (sz[a] < sz[b]) swap(a, b);
        p[b] = a;
        sz[a] += sz[b];
        return true;
    }
    bool same(int a, int b) { return find(a) == find(b); }
};

static int owned_count(int total, int agents, int id) {
    return (int)((1LL * total * (id + 1)) / agents - (1LL * total * id) / agents);
}
static int bits_per_message(int len) {
    Big capacity; capacity.limb.push_back(1);
    for (int i = 0; i < len; ++i) capacity.mul_small_add(95, 0);
    return capacity.bit_length() - 1;
}
static string encode_chunk(const vector<unsigned char> &bits, int from, int count, int len) {
    Big x; for (int i = 0; i < count; ++i) x.shl1_or(bits[from + i]);
    string body(len, ' ');
    for (int i = len - 1; i >= 0; --i) { int digit = (int)x.div_small(95); body[i] = char(32 + digit); }
    return body;
}
static vector<unsigned char> decode_chunk(const string &body, int count) {
    Big x; for (unsigned char ch : body) { if (ch < 32 || ch > 126) continue; x.mul_small_add(95, int(ch - 32)); }
    vector<unsigned char> bits; bits.reserve(count);
    for (int i = count - 1; i >= 0; --i) bits.push_back(x.bit(i) ? 1 : 0);
    return bits;
}

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

    int n, num_agents, agent_id, max_msg_len;
    if (!(cin >> n >> num_agents >> agent_id >> max_msg_len)) return 0;

    if (n == 1) {
        if (agent_id == 0) cout << "! " << endl; else cout << "halt" << endl;
        return 0;
    }
    if (max_msg_len <= 0) { cout << "halt" << endl; return 0; }

    const int m = (n + 1) / 2;              // rooms per side
    const int H = m * (m - 1);              // horizontal corridors
    const int V = (m - 1) * m;              // vertical corridors
    const int total = H + V;                // == (n^2-1)/2

    struct Edge {
        int gr, gc;
        int a, b;
        int f1, f2;
        int old_idx;
    };
    const int inner_faces = (m - 1) * (m - 1);
    const int outer_face = inner_faces;
    auto face_id = [&](int i, int j) { return i * (m - 1) + j; };
    vector<Edge> edges;
    edges.reserve(total);
    for (int gr = 1; gr <= n; ++gr) {
        for (int gc = 1; gc <= n; ++gc) {
            bool row_room = gr % 2 == 1;
            bool col_room = gc % 2 == 1;
            if (row_room == col_room) continue;
            if (row_room) {
                int i = (gr - 1) / 2;
                int j = (gc - 2) / 2;
                int upper = i > 0 ? face_id(i - 1, j) : outer_face;
                int lower = i + 1 < m ? face_id(i, j) : outer_face;
                int old_idx = i * (m - 1) + j;
                edges.push_back({gr, gc, i * m + j, i * m + (j + 1), upper, lower, old_idx});
            } else {
                int i = (gr - 2) / 2;
                int j = (gc - 1) / 2;
                int left = j > 0 ? face_id(i, j - 1) : outer_face;
                int right = j + 1 < m ? face_id(i, j) : outer_face;
                int old_idx = H + i * m + j;
                edges.push_back({gr, gc, i * m + j, (i + 1) * m + j, left, right, old_idx});
            }
        }
    }
    auto z_order = [](int r, int c) {
        uint64_t z = 0;
        for (int b = 0; b < 16; ++b) {
            z |= ((uint64_t)((c >> b) & 1) << (2 * b));
            z |= ((uint64_t)((r >> b) & 1) << (2 * b + 1));
        }
        return z;
    };
    auto diag_band = [](int d) {
        if (d <= 2) return 0;
        int b = 1, lim = 4;
        while (d > lim) {
            ++b;
            lim <<= 1;
        }
        return b;
    };
    auto edge_score = [&](const Edge &e) {
        int ar = e.a / m, ac = e.a % m;
        int br = e.b / m, bc = e.b % m;
        int s1 = ar + ac + 1 + (m - 1 - br) + (m - 1 - bc);
        int s2 = br + bc + 1 + (m - 1 - ar) + (m - 1 - ac);
        return min(s1, s2);
    };
    sort(edges.begin(), edges.end(), [&](const Edge &x, const Edge &y) {
        int xr = (x.a / m + x.b / m) / 2;
        int xc = (x.a % m + x.b % m) / 2;
        int yr = (y.a / m + y.b / m) / 2;
        int yc = (y.a % m + y.b % m) / 2;
        int bx = diag_band(abs(xr - xc));
        int by = diag_band(abs(yr - yc));
        if (bx != by) return bx < by;
        int sx = edge_score(x), sy = edge_score(y);
        if (sx != sy) return sx < sy;
        bool x_forward = x.b == x.a + 1 || x.b == x.a + m;
        bool y_forward = y.b == y.a + 1 || y.b == y.a + m;
        if (x_forward != y_forward) return x_forward > y_forward;
        uint64_t zx = z_order(xr, xc), zy = z_order(yr, yc);
        if (zx != zy) return zx < zy;
        return x.old_idx < y.old_idx;
    });

    auto query = [&](int r, int c) {
        cout << "? " << r << ' ' << c << endl;
        string reply;
        if (!(cin >> reply)) exit(0);
        return (unsigned char)(reply == "1");
    };

    const int per_message = bits_per_message(max_msg_len);
    const int batch = max(1, min(64, per_message));
    const int workers = max(1, num_agents - 1);
    const int tile = 128;
    vector<vector<int>> worker_positions(num_agents);
    for (int block = 0, owner = 1; block < total; block += tile, ++owner) {
        int id = 1 + ((owner - 1) % workers);
        int until = min(total, block + tile);
        worker_positions[id].reserve(worker_positions[id].size() + (until - block));
        for (int pos = block; pos < until; ++pos) worker_positions[id].push_back(pos);
    }

    // --- Workers: scan their static chunk and stream inferred/queried statuses ---
    if (agent_id != 0) {
        int pos = 0, end = (int)worker_positions[agent_id].size();
        vector<unsigned char> pending;
        pending.reserve(batch);
        Dsu room_dsu(m * m);
        Dsu face_dsu(inner_faces + 1);

        auto add_bit = [&](const Edge &e, unsigned char bit) {
            pending.push_back(bit);
            if (bit) room_dsu.unite(e.a, e.b);
            else face_dsu.unite(e.f1, e.f2);
        };

        while (true) {
            bool need_query = false;
            Edge qe{};
            while ((int)pending.size() < batch && pos < end) {
                const Edge &e = edges[worker_positions[agent_id][pos++]];
                if (room_dsu.same(e.a, e.b)) {
                    add_bit(e, 0);
                } else if (face_dsu.same(e.f1, e.f2)) {
                    add_bit(e, 1);
                } else {
                    qe = e;
                    need_query = true;
                    break;
                }
            }
            if ((int)pending.size() >= batch) {
                cout << "> 0 " << encode_chunk(pending, 0, (int)pending.size(), max_msg_len) << endl;
                string reply;
                if (!(cin >> reply)) return 0;
                pending.clear();
                continue;
            }
            if (need_query) {
                unsigned char bit = query(qe.gr, qe.gc);
                add_bit(qe, bit);
                continue;
            }
            if (!pending.empty()) {
                cout << "> 0 " << encode_chunk(pending, 0, (int)pending.size(), max_msg_len) << endl;
                string reply;
                if (!(cin >> reply)) return 0;
                pending.clear();
                continue;
            }
            cout << "halt" << endl;
            return 0;
        }
    }

    // --- Coordinator: collect batches and claim as soon as open edges connect endpoints ---
    vector<unsigned char> open(total, 0);
    vector<int> received(num_agents, 0), remaining(num_agents, 0);
    int total_remaining = 0;
    for (int id = 1; id < num_agents; ++id) {
        int cnt = (int)worker_positions[id].size();
        remaining[id] = (cnt + batch - 1) / batch;
        total_remaining += remaining[id];
    }

    Dsu global_rooms(m * m);
    vector<vector<pair<int, char>>> open_adj(m * m);

    auto build_and_claim = [&]() {
        vector<int> parent(m * m, -1);
        vector<char> pstep(m * m, 0);
        queue<int> q;
        parent[0] = 0;
        q.push(0);
        while (!q.empty() && parent[m * m - 1] == -1) {
            int v = q.front();
            q.pop();
            for (auto [to, ch] : open_adj[v]) {
                if (parent[to] != -1) continue;
                parent[to] = v;
                pstep[to] = ch;
                q.push(to);
                if (to == m * m - 1) break;
            }
        }
        if (parent[m * m - 1] == -1) return false;
        string room_path;
        for (int v = m * m - 1; v != 0; v = parent[v]) room_path.push_back(pstep[v]);
        reverse(room_path.begin(), room_path.end());
        string grid_path;
        grid_path.reserve(room_path.size() * 2);
        for (char ch : room_path) {
            grid_path.push_back(ch);
            grid_path.push_back(ch);
        }
        cout << "! " << grid_path << endl;
        return true;
    };

    auto add_open_edge = [&](const Edge &e) {
        if (global_rooms.unite(e.a, e.b)) {
            int ar = e.a / m, ac = e.a % m;
            int br = e.b / m, bc = e.b % m;
            char ab = 0, ba = 0;
            if (br == ar && bc == ac + 1) { ab = 'R'; ba = 'L'; }
            else if (br == ar && bc == ac - 1) { ab = 'L'; ba = 'R'; }
            else if (br == ar + 1 && bc == ac) { ab = 'D'; ba = 'U'; }
            else { ab = 'U'; ba = 'D'; }
            open_adj[e.a].push_back({e.b, ab});
            open_adj[e.b].push_back({e.a, ba});
        }
    };

    string line;
    while (total_remaining > 0) {
        cout << "< ?" << endl;
        if (!getline(cin >> ws, line)) return 0;
        if (line == "- -") continue;
        size_t sep = line.find(' ');
        if (sep == string::npos) continue;
        int sender = stoi(line.substr(0, sep));
        string body = line.substr(sep + 1);
        if (sender <= 0 || sender >= num_agents || remaining[sender] <= 0) continue;
        int cnt = (int)worker_positions[sender].size();
        int take = min(batch, cnt - received[sender]);
        auto bits = decode_chunk(body, take);
        for (int i = 0; i < take; ++i) {
            const Edge &e = edges[worker_positions[sender][received[sender] + i]];
            open[e.old_idx] = bits[i];
            if (bits[i]) add_open_edge(e);
        }
        received[sender] += take;
        --remaining[sender];
        --total_remaining;
        if (global_rooms.same(0, m * m - 1) && build_and_claim()) return 0;
    }

    if (build_and_claim()) return 0;
    cout << "halt" << endl;
    return 0;

#if 0
    // Old full-scan reconstruction kept disabled for reference.
    vector<unsigned char> open(total, 0);
    { int p = 0; for (int pos = my_l; pos < my_r; ++pos) open[edges[pos].old_idx] = my_bits[p++]; }
            string reply;
            if (!(cin >> reply)) return 0;
        }
        cout << "halt" << endl;
        return 0;
    }

    // --- Phase 2 (agent 0): reassemble full corridor bitmap ---
    vector<unsigned char> open(total, 0);
    { int p = 0; for (int pos = my_l; pos < my_r; ++pos) open[edges[pos].old_idx] = my_bits[p++]; }

    vector<vector<string>> msgs(num_agents);
    vector<int> remaining(num_agents, 0);
    int total_remaining = 0;
    for (int id = 1; id < num_agents; ++id) {
        int cnt = owned_count(total, num_agents, id);
        remaining[id] = (cnt + per_message - 1) / per_message;
        total_remaining += remaining[id];
        msgs[id].reserve(remaining[id]);
    }

    string line;
    while (total_remaining > 0) {
        cout << "< ?" << endl;
        if (!getline(cin >> ws, line)) return 0;
        if (line == "- -") continue;
        size_t sep = line.find(' ');
        if (sep == string::npos) continue;
        int sender = stoi(line.substr(0, sep));
        string body = line.substr(sep + 1);
        if (1 <= sender && sender < num_agents && remaining[sender] > 0) {
            msgs[sender].push_back(body);
            --remaining[sender];
            --total_remaining;
        }
    }

    for (int id = 1; id < num_agents; ++id) {
        int cnt = owned_count(total, num_agents, id);
        vector<unsigned char> bits; bits.reserve(cnt);
        for (const string &body : msgs[id]) {
            int take = min(per_message, cnt - (int)bits.size());
            auto chunk = decode_chunk(body, take);
            bits.insert(bits.end(), chunk.begin(), chunk.end());
        }
        int p = 0;
        int l = (int)(1LL * total * id / num_agents);
        int r = (int)(1LL * total * (id + 1) / num_agents);
        for (int pos = l; pos < r; ++pos) open[edges[pos].old_idx] = bits[p++];
    }

    // --- Phase 3: BFS the room tree, reconstruct path ---
    auto h_open = [&](int i, int j) { return open[i * (m - 1) + j] != 0; };        // room(i,j)-(i,j+1)
    auto v_open = [&](int i, int j) { return open[H + i * m + j] != 0; };          // room(i,j)-(i+1,j)

    vector<vector<char>> seen(m, vector<char>(m, 0));
    vector<vector<pair<int,int>>> parent(m, vector<pair<int,int>>(m, {-1,-1}));
    vector<vector<char>> pstep(m, vector<char>(m, 0));
    queue<pair<int,int>> q;
    seen[0][0] = 1; q.push({0, 0});
    while (!q.empty() && !seen[m-1][m-1]) {
        auto [i, j] = q.front(); q.pop();
        // R
        if (j + 1 < m && h_open(i, j) && !seen[i][j+1]) { seen[i][j+1]=1; parent[i][j+1]={i,j}; pstep[i][j+1]='R'; q.push({i,j+1}); }
        // L
        if (j - 1 >= 0 && h_open(i, j-1) && !seen[i][j-1]) { seen[i][j-1]=1; parent[i][j-1]={i,j}; pstep[i][j-1]='L'; q.push({i,j-1}); }
        // D
        if (i + 1 < m && v_open(i, j) && !seen[i+1][j]) { seen[i+1][j]=1; parent[i+1][j]={i,j}; pstep[i+1][j]='D'; q.push({i+1,j}); }
        // U
        if (i - 1 >= 0 && v_open(i-1, j) && !seen[i-1][j]) { seen[i-1][j]=1; parent[i-1][j]={i,j}; pstep[i-1][j]='U'; q.push({i-1,j}); }
    }

    if (!seen[m-1][m-1]) { cout << "halt" << endl; return 0; }

    string room_path;
    for (int i = m-1, j = m-1; !(i == 0 && j == 0);) {
        room_path.push_back(pstep[i][j]);
        auto [pi, pj] = parent[i][j];
        i = pi; j = pj;
    }
    reverse(room_path.begin(), room_path.end());

    string grid_path; grid_path.reserve(room_path.size() * 2);
    for (char ch : room_path) { grid_path.push_back(ch); grid_path.push_back(ch); }

    cout << "! " << grid_path << endl;
    return 0;
#endif
}
