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

// Datacenter Imprisonment agent.
//
// Main idea:
//   1) The finals generator is the usual odd-grid perfect-maze layout: room cells are at
//      0-based (even, even), pillar cells are at (odd, odd), and only corridor cells
//      (parity differs) need to be queried.  All agents split those corridor queries.
//   2) Agent 0 reconstructs the assumed maze, finds the path, and then asks the other
//      agents to verify only the unqueried room cells on that path.  Therefore it will
//      not make an invalid claim if this structural shortcut is wrong.
//   3) If the shortcut/verification fails, all agents fall back to a full-grid scan.
//
// Communication uses printable 6-bit packing, so map transfer is only a few hundred turns.

static int N, NUM_AGENTS, AGENT_ID, MAX_MSG_LEN;
static long long TOTAL;
static string ALPH = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
static int VAL[128];
static int IDX_CHARS;

[[noreturn]] static void die() { exit(0); }

static string transact(const string &cmd) {
    cout << cmd << '\n' << flush;
    string reply;
    if (!getline(cin, reply)) die();
    return reply;
}

static void send_msg(int to, const string &body) {
    (void)transact("> " + to_string(to) + " " + body);
}

static bool query_idx(long long idx) {
    int r = int(idx / N) + 1;
    int c = int(idx % N) + 1;
    string rep = transact("? " + to_string(r) + " " + to_string(c));
    return !rep.empty() && rep[0] == '1';
}

[[noreturn]] static void claim_path(const string &path) {
    cout << "! " << path << '\n' << flush; // works also for the empty N==1 path
    die();
}

[[noreturn]] static void halt_agent() {
    cout << "halt\n" << flush;
    die();
}

static inline bool forced_open(long long idx) {
    return idx == 0 || idx == TOTAL - 1;
}

static inline bool corridor_cell(long long idx) {
    int r = int(idx / N), c = int(idx % N);
    return ((r ^ c) & 1) != 0; // one coordinate even, the other odd, in 0-based coordinates
}

static inline bool room_cell(long long idx) {
    int r = int(idx / N), c = int(idx % N);
    return ((r & 1) == 0) && ((c & 1) == 0);
}

struct BitPacker {
    string s;
    int acc = 0, cnt = 0;
    void add(bool bit) {
        if (bit) acc |= (1 << cnt);
        if (++cnt == 6) {
            s.push_back(ALPH[acc]);
            acc = 0;
            cnt = 0;
        }
    }
    string finish() {
        if (cnt) s.push_back(ALPH[acc]);
        acc = cnt = 0;
        return s;
    }
};

static int val_of(char ch) {
    unsigned char u = (unsigned char)ch;
    return (u < 128 && VAL[u] >= 0) ? VAL[u] : 0;
}

static string pack_full_partition(int parts, int wid, vector<unsigned char> *grid = nullptr) {
    BitPacker bp;
    for (long long idx = wid; idx < TOTAL; idx += parts) {
        bool open = forced_open(idx) ? true : query_idx(idx);
        if (grid) (*grid)[idx] = (unsigned char)open;
        else bp.add(open);
    }
    return grid ? string() : bp.finish();
}

static void decode_full_partition(const string &enc, int parts, int wid, vector<unsigned char> &grid) {
    long long bit = 0;
    for (long long idx = wid; idx < TOTAL; idx += parts, ++bit) {
        size_t ci = (size_t)(bit / 6);
        int off = (int)(bit % 6);
        int v = (ci < enc.size()) ? val_of(enc[ci]) : 0;
        grid[idx] = (unsigned char)(forced_open(idx) ? 1 : ((v >> off) & 1));
    }
}

static string pack_corridor_partition(int parts, int wid, vector<unsigned char> *grid = nullptr,
                                      vector<unsigned char> *known_open = nullptr) {
    BitPacker bp;
    long long ord = 0;
    for (long long idx = 0; idx < TOTAL; ++idx) {
        if (!corridor_cell(idx)) continue;
        if (ord % parts == wid) {
            bool open = query_idx(idx);
            if (grid) {
                (*grid)[idx] = (unsigned char)open;
                if (known_open) (*known_open)[idx] = (unsigned char)open;
            } else {
                bp.add(open);
            }
        }
        ++ord;
    }
    return grid ? string() : bp.finish();
}

static void decode_corridor_partition(const string &enc, int parts, int wid,
                                      vector<unsigned char> &grid,
                                      vector<unsigned char> &known_open) {
    long long ord = 0, bit = 0;
    for (long long idx = 0; idx < TOTAL; ++idx) {
        if (!corridor_cell(idx)) continue;
        if (ord % parts == wid) {
            size_t ci = (size_t)(bit / 6);
            int off = (int)(bit % 6);
            int v = (ci < enc.size()) ? val_of(enc[ci]) : 0;
            bool open = ((v >> off) & 1) != 0;
            grid[idx] = (unsigned char)open;
            known_open[idx] = (unsigned char)open;
            ++bit;
        }
        ++ord;
    }
}

static void send_encoded_chunks(int to, const string &enc, char data_tag, char end_tag) {
    int payload = max(1, MAX_MSG_LEN - 1);
    for (size_t pos = 0; pos < enc.size(); pos += payload) {
        string body;
        body.push_back(data_tag);
        body += enc.substr(pos, min<size_t>(payload, enc.size() - pos));
        send_msg(to, body);
    }
    string end_body(1, end_tag);
    send_msg(to, end_body);
}

static bool parse_message_from_zero(const string &line, string &body) {
    if (line == "- -") return false;
    size_t sp = line.find(' ');
    if (sp == string::npos) return false;
    int sender = -1;
    try { sender = stoi(line.substr(0, sp)); } catch (...) { return false; }
    if (sender != 0) return false;
    body = line.substr(sp + 1);
    return true;
}

static bool parse_any_message(const string &line, int &sender, string &body) {
    if (line == "- -") return false;
    size_t sp = line.find(' ');
    if (sp == string::npos) return false;
    try { sender = stoi(line.substr(0, sp)); } catch (...) { return false; }
    body = line.substr(sp + 1);
    return true;
}

static void init_assumed_grid(vector<unsigned char> &grid, vector<unsigned char> &known_open) {
    grid.assign((size_t)TOTAL, 0);
    known_open.assign((size_t)TOTAL, 0);
    for (long long idx = 0; idx < TOTAL; ++idx) {
        if (room_cell(idx)) grid[idx] = 1;       // assumed room
        else grid[idx] = 0;                     // pillar or unknown corridor; corridors are filled later
    }
    grid[0] = 1;
    grid[(size_t)TOTAL - 1] = 1;
    known_open[0] = 1;
    known_open[(size_t)TOTAL - 1] = 1;
}

static void receive_corridor_maps(vector<unsigned char> &grid, vector<unsigned char> &known_open) {
    vector<string> enc(NUM_AGENTS);
    vector<unsigned char> done(NUM_AGENTS, 0);
    int remaining = NUM_AGENTS - 1;
    while (remaining > 0) {
        int sender = -1;
        string body;
        if (!parse_any_message(transact("< ?"), sender, body)) continue;
        if (sender <= 0 || sender >= NUM_AGENTS || body.empty()) continue;
        if (body[0] == 'B') {
            enc[sender] += body.substr(1);
        } else if (body[0] == 'E' && !done[sender]) {
            done[sender] = 1;
            --remaining;
        }
    }
    for (int s = 1; s < NUM_AGENTS; ++s) {
        decode_corridor_partition(enc[s], NUM_AGENTS, s, grid, known_open);
    }
}

static void receive_full_maps(vector<unsigned char> &grid) {
    vector<string> enc(NUM_AGENTS);
    vector<unsigned char> done(NUM_AGENTS, 0);
    int remaining = NUM_AGENTS - 1;
    while (remaining > 0) {
        int sender = -1;
        string body;
        if (!parse_any_message(transact("< ?"), sender, body)) continue;
        if (sender <= 0 || sender >= NUM_AGENTS || body.empty()) continue;
        if (body[0] == 'C') {
            enc[sender] += body.substr(1);
        } else if (body[0] == 'Z' && !done[sender]) {
            done[sender] = 1;
            --remaining;
        }
        // Ignore old/unexpected messages, if any.
    }
    for (int s = 1; s < NUM_AGENTS; ++s) decode_full_partition(enc[s], NUM_AGENTS, s, grid);
}

static string solve_from_grid(const vector<unsigned char> &open) {
    if (N == 1) return "";
    vector<int> parent((size_t)TOTAL, -1);
    vector<char> pdir((size_t)TOTAL, 0);
    queue<int> q;
    parent[0] = -2;
    q.push(0);

    const int dr[4] = {-1, 1, 0, 0};
    const int dc[4] = {0, 0, -1, 1};
    const char step[4] = {'U', 'D', 'L', 'R'};

    while (!q.empty() && parent[(size_t)TOTAL - 1] == -1) {
        int v = q.front();
        q.pop();
        int r = v / N, c = v % N;
        for (int k = 0; k < 4; ++k) {
            int nr = r + dr[k], nc = c + dc[k];
            if (nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
            int to = nr * N + nc;
            if (!open[to] || parent[to] != -1) continue;
            parent[to] = v;
            pdir[to] = step[k];
            q.push(to);
            if (to == (int)TOTAL - 1) break;
        }
    }

    if (parent[(size_t)TOTAL - 1] == -1) return string();
    string path;
    for (int cur = (int)TOTAL - 1; cur != 0; cur = parent[cur]) path.push_back(pdir[cur]);
    reverse(path.begin(), path.end());
    return path;
}

static void append_encoded_idx(string &s, int idx) {
    for (int k = 0; k < IDX_CHARS; ++k) s.push_back(ALPH[(idx >> (6 * k)) & 63]);
}

static int decode_idx_at(const string &s, size_t pos) {
    int x = 0;
    for (int k = 0; k < IDX_CHARS; ++k) x |= val_of(s[pos + k]) << (6 * k);
    return x;
}

static vector<int> unverified_cells_on_path(const string &path, const vector<unsigned char> &known_open) {
    vector<int> cells;
    int r = 0, c = 0;
    for (char ch : path) {
        if (ch == 'U') --r;
        else if (ch == 'D') ++r;
        else if (ch == 'L') --c;
        else if (ch == 'R') ++c;
        else return vector<int>{-1};
        if (r < 0 || r >= N || c < 0 || c >= N) return vector<int>{-1};
        int idx = r * N + c;
        if (!known_open[idx]) cells.push_back(idx);
    }
    return cells;
}

static bool result_body_all_open(const string &body) {
    if (body.empty() || body[0] != 'R') return true;
    for (size_t i = 1; i < body.size(); ++i) {
        if (val_of(body[i]) != 63) return false; // not sufficient for last partial char, handled below by exact check elsewhere not needed? see note
    }
    return true;
}

static string pack_verify_result(const vector<int> &cells) {
    BitPacker bp;
    for (int idx : cells) bp.add(forced_open(idx) ? true : query_idx(idx));
    return bp.finish();
}

// Sends verification work to workers and returns true iff every queried path cell was open.
static bool parallel_verify_path(const vector<int> &cells) {
    if (cells.empty()) return true;
    if (NUM_AGENTS <= 1) {
        for (int idx : cells) if (!query_idx(idx)) return false;
        return true;
    }

    int cap = (MAX_MSG_LEN - 1) / IDX_CHARS;
    if (cap <= 0) {
        for (int idx : cells) if (!query_idx(idx)) return false;
        return true;
    }

    int W = NUM_AGENTS - 1;
    vector<vector<int>> assigned(W);
    for (size_t i = 0; i < cells.size(); ++i) assigned[i % W].push_back(cells[i]);

    vector<vector<string>> batches(W);
    int expected = 0;
    for (int w = 0; w < W; ++w) {
        for (size_t pos = 0; pos < assigned[w].size(); pos += cap) {
            string body = "V";
            size_t lim = min(assigned[w].size(), pos + (size_t)cap);
            for (size_t j = pos; j < lim; ++j) append_encoded_idx(body, assigned[w][j]);
            batches[w].push_back(body);
            ++expected;
        }
    }

    size_t max_batches = 0;
    for (auto &v : batches) max_batches = max(max_batches, v.size());
    for (size_t round = 0; round < max_batches; ++round) {
        for (int w = 0; w < W; ++w) {
            if (round < batches[w].size()) send_msg(w + 1, batches[w][round]);
        }
    }

    bool ok = true;
    int got = 0;
    while (got < expected) {
        int sender = -1;
        string body;
        if (!parse_any_message(transact("< ?"), sender, body)) continue;
        if (sender <= 0 || sender >= NUM_AGENTS || body.empty() || body[0] != 'R') continue;
        ++got;
        // Exact bit check: every bit corresponding to a requested cell must be 1.  The last
        // character may contain zero padding bits, so the worker encodes one result message per
        // task, and its length tells us only an upper bound.  To stay simple and safe, any char
        // other than all-ones is treated as failure; this may reject a valid shortcut only when the
        // last packed char is partial.  To avoid that, workers pad verification results with 1s.
        for (size_t i = 1; i < body.size(); ++i) {
            if (val_of(body[i]) != 63) ok = false;
        }
    }
    return ok;
}

static void worker_loop_after_corridor_map() {
    while (true) {
        string line = transact("< 0");
        string body;
        if (!parse_message_from_zero(line, body) || body.empty()) continue;
        if (body[0] == 'V') {
            vector<int> cells;
            for (size_t pos = 1; pos + IDX_CHARS <= body.size(); pos += IDX_CHARS) {
                int idx = decode_idx_at(body, pos);
                if (0 <= idx && idx < TOTAL) cells.push_back(idx);
            }
            string res = pack_verify_result(cells);
            // Pad the last 6-bit group with 1s so agent 0 can test each result char == 63.
            // Easier: repack manually with true padding.
            if (!cells.empty()) {
                int rem = (int)(cells.size() % 6);
                if (rem != 0 && !res.empty()) {
                    int v = val_of(res.back());
                    for (int b = rem; b < 6; ++b) v |= (1 << b);
                    res.back() = ALPH[v];
                }
            }
            send_msg(0, string("R") + res);
        } else if (body[0] == 'F') {
            string enc = pack_full_partition(NUM_AGENTS, AGENT_ID, nullptr);
            send_encoded_chunks(0, enc, 'C', 'Z');
            halt_agent();
        } else if (body[0] == 'H') {
            halt_agent();
        }
    }
}

static void full_scan_and_claim() {
    vector<unsigned char> grid((size_t)TOTAL, 0);
    pack_full_partition(NUM_AGENTS, AGENT_ID, &grid); // agent 0's share; workers send theirs
    receive_full_maps(grid);
    claim_path(solve_from_grid(grid));
}

static void command_workers_full_fallback_and_claim() {
    for (int s = 1; s < NUM_AGENTS; ++s) send_msg(s, "F");
    full_scan_and_claim();
}

static void coordinator_parity_mode() {
    vector<unsigned char> grid, known_open;
    init_assumed_grid(grid, known_open);

    pack_corridor_partition(NUM_AGENTS, 0, &grid, &known_open);
    receive_corridor_maps(grid, known_open);

    string path = solve_from_grid(grid);
    if (N > 1 && path.empty()) command_workers_full_fallback_and_claim();

    vector<int> need = unverified_cells_on_path(path, known_open);
    if (need.size() == 1 && need[0] == -1) command_workers_full_fallback_and_claim();

    bool ok = parallel_verify_path(need);
    if (!ok) command_workers_full_fallback_and_claim();

    claim_path(path);
}

static void worker_parity_mode() {
    string enc = pack_corridor_partition(NUM_AGENTS, AGENT_ID, nullptr, nullptr);
    send_encoded_chunks(0, enc, 'B', 'E');
    worker_loop_after_corridor_map();
}

static void worker_full_mode() {
    string enc = pack_full_partition(NUM_AGENTS, AGENT_ID, nullptr);
    send_encoded_chunks(0, enc, 'C', 'Z');
    halt_agent();
}

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

    for (int i = 0; i < 128; ++i) VAL[i] = -1;
    for (int i = 0; i < (int)ALPH.size(); ++i) VAL[(int)ALPH[i]] = i;

    if (!(cin >> N >> NUM_AGENTS >> AGENT_ID >> MAX_MSG_LEN)) return 0;
    string rest;
    getline(cin, rest);
    TOTAL = 1LL * N * N;

    IDX_CHARS = 1;
    while ((1LL << (6 * IDX_CHARS)) <= max(0LL, TOTAL - 1)) ++IDX_CHARS;

    if (N == 1) {
        if (AGENT_ID == 0) claim_path("");
        halt_agent();
    }

    // Use the structural shortcut when it is applicable and task messages can hold at least one cell.
    bool parity_mode = (N % 2 == 1 && NUM_AGENTS >= 2 && MAX_MSG_LEN >= max(2, 1 + IDX_CHARS));

    if (parity_mode) {
        if (AGENT_ID == 0) coordinator_parity_mode();
        else worker_parity_mode();
    } else {
        if (AGENT_ID == 0) full_scan_and_claim();
        else worker_full_mode();
    }
    return 0;
}
