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

struct DSU {
    vector<int> p, sz;
    explicit DSU(int n = 0) { init(n); }
    void init(int n) {
        p.resize(n);
        sz.assign(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); }
};

void push13(string &out, int value) {
    out.push_back(char(32 + (value % 95)));
    out.push_back(char(32 + (value / 95)));
}

int val95(char ch) {
    return int((unsigned char)ch) - 32;
}

struct MazeModel {
    int n, m, hcnt, ecnt;
    DSU dsu;
    vector<vector<pair<int, char>>> adj;

    explicit MazeModel(int n_)
        : n(n_), m((n + 1) / 2), hcnt(m * max(0, m - 1)),
          ecnt(2 * m * max(0, m - 1)), dsu(m * m), adj(m * m) {}

    pair<int, int> edge_rooms(int id) const {
        if (id < hcnt) {
            int r = id / (m - 1);
            int c = id % (m - 1);
            int a = r * m + c;
            return {a, a + 1};
        }
        int k = id - hcnt;
        int r = k / m;
        int c = k % m;
        int a = r * m + c;
        return {a, a + m};
    }

    pair<int, int> edge_cell(int id) const {
        if (id < hcnt) {
            int r = id / (m - 1);
            int c = id % (m - 1);
            return {2 * r + 1, 2 * c + 2};
        }
        int k = id - hcnt;
        int r = k / m;
        int c = k % m;
        return {2 * r + 2, 2 * c + 1};
    }

    void add_edge(int id) {
        if (id < 0 || id >= ecnt) return;
        auto [a, b] = edge_rooms(id);
        char ab = id < hcnt ? 'R' : 'D';
        char ba = id < hcnt ? 'L' : 'U';
        adj[a].push_back({b, ab});
        adj[b].push_back({a, ba});
        dsu.unite(a, b);
    }

    bool connected() { return dsu.same(0, m * m - 1); }

    string build_path() const {
        int total = m * m;
        vector<int> parent(total, -1);
        vector<char> move_to(total, 0);
        queue<int> q;
        q.push(0);
        parent[0] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            if (v == total - 1) break;
            for (auto [to, ch] : adj[v]) {
                if (parent[to] == -1) {
                    parent[to] = v;
                    move_to[to] = ch;
                    q.push(to);
                }
            }
        }
        string rooms;
        for (int cur = total - 1; cur != 0; cur = parent[cur]) rooms.push_back(move_to[cur]);
        reverse(rooms.begin(), rooms.end());
        string path;
        path.reserve(rooms.size() * 2);
        for (char ch : rooms) {
            path.push_back(ch);
            path.push_back(ch);
        }
        return path;
    }
};

vector<int> build_edge_order(const MazeModel &model) {
    vector<int> order(model.ecnt);
    iota(order.begin(), order.end(), 0);
    int hcnt = model.hcnt;
    int m = model.m;
    auto key = [&](int edge) {
        int rr2, cc2, orient;
        if (edge < hcnt) {
            int r = edge / (m - 1);
            int c = edge % (m - 1);
            rr2 = 2 * r;
            cc2 = 2 * c + 1;
            orient = 0;
        } else {
            int k = edge - hcnt;
            int r = k / m;
            int c = k % m;
            rr2 = 2 * r + 1;
            cc2 = 2 * c;
            orient = 1;
        }
        return tuple<int, int, int, int>(abs(rr2 - cc2), rr2 + cc2, orient, edge);
    };
    sort(order.begin(), order.end(), [&](int a, int b) { return key(a) < key(b); });
    return order;
}

string ask_line(const string &cmd) {
    cout << cmd << '\n' << flush;
    string reply;
    if (!getline(cin, reply)) exit(0);
    return reply;
}

void send_to_zero(const string &body) {
    string reply = ask_line("> 0 " + body);
    (void)reply;
}

void send_to_zero_no_wait(const string &body) {
    cout << "> 0 " << body << '\n' << flush;
}

string pack_bits(const vector<unsigned char> &bits) {
    string packed;
    int bit_pos = 0;
    int cur = 0;
    for (unsigned char bit : bits) {
        if (bit) cur |= 1 << (bit_pos % 13);
        ++bit_pos;
        if (bit_pos % 13 == 0) {
            push13(packed, cur);
            cur = 0;
        }
    }
    if (bit_pos % 13 != 0) push13(packed, cur);
    return packed;
}

int slice_len(int ecnt, int agents, int sender) {
    if (sender >= ecnt) return 0;
    return (ecnt + agents - 1 - sender) / agents;
}

int phased_block_size(int ecnt, int agents, int max_msg_len) {
    int q = (ecnt + agents - 1) / agents;
    int b;
    if (q >= 130 && q <= 800) {
        b = max(1, (q * 80 + 99) / 100);
    } else {
        int by_q = q / 2;
        b = min(1100, max(650, by_q));
    }
    int max_bits = max(13, (max_msg_len / 2) * 13);
    b = min(b, max_bits);
    return max(1, min(q, b));
}

void worker(int n, int agents, int id, int max_msg_len, const vector<int> &order) {
    MazeModel model(n);
    string packed;
    int bit_pos = 0;
    int cur = 0;
    for (int pos = id; pos < model.ecnt; pos += agents) {
        int edge = order[pos];
        auto [r, c] = model.edge_cell(edge);
        string reply = ask_line("? " + to_string(r) + " " + to_string(c));
        if (!reply.empty() && reply[0] == '1') cur |= 1 << (bit_pos % 13);
        ++bit_pos;
        if (bit_pos % 13 == 0) {
            push13(packed, cur);
            cur = 0;
        }
    }
    if (bit_pos % 13 != 0) push13(packed, cur);

    int chunk = max(2, (max_msg_len / 2) * 2);
    for (int pos = 0; pos < (int)packed.size(); pos += chunk) {
        string body = packed.substr(pos, chunk);
        if (pos + chunk >= (int)packed.size()) {
            send_to_zero_no_wait(body);
        } else {
            send_to_zero(body);
        }
    }
}

void worker_phased(int n, int agents, int id, int max_msg_len, const vector<int> &order, int block) {
    MazeModel model(n);
    int total = slice_len(model.ecnt, agents, id);
    for (int start = 0; start < total; start += block) {
        int take = min(block, total - start);
        vector<unsigned char> bits;
        bits.reserve(take);
        for (int off = 0; off < take; ++off) {
            int logical = id + (start + off) * agents;
            int edge = order[logical];
            auto [r, c] = model.edge_cell(edge);
            string reply = ask_line("? " + to_string(r) + " " + to_string(c));
            bits.push_back((!reply.empty() && reply[0] == '1') ? 1 : 0);
        }
        string body = pack_bits(bits);
        if (start + take >= total) send_to_zero_no_wait(body);
        else send_to_zero(body);
    }
}

void coordinator(int n, int agents, int id, int max_msg_len, const vector<int> &order) {
    MazeModel model(n);
    vector<int> bit_index(agents, 0);
    vector<int> chunks_left(agents, 0);
    int pending_chunks = 0;
    int chunk = max(2, (max_msg_len / 2) * 2);
    for (int sender = 1; sender < agents; ++sender) {
        int q = (model.ecnt + agents - 1 - sender) / agents;
        int chars = 2 * ((q + 12) / 13);
        chunks_left[sender] = (chars + chunk - 1) / chunk;
        pending_chunks += chunks_left[sender];
    }

    auto claim_if_ready = [&]() -> bool {
        if (!model.connected()) return false;
        cout << "! " << model.build_path() << '\n' << flush;
        return true;
    };

    if (n == 1) {
        cout << "! " << '\n' << flush;
        return;
    }

    for (int pos = id; pos < model.ecnt; pos += agents) {
        int edge = order[pos];
        auto [r, c] = model.edge_cell(edge);
        string reply = ask_line("? " + to_string(r) + " " + to_string(c));
        if (!reply.empty() && reply[0] == '1') {
            model.add_edge(edge);
            if (claim_if_ready()) return;
        }
    }

    while (pending_chunks > 0) {
        string reply = ask_line("< ?");
        if (reply == "- -") continue;
        size_t sp = reply.find(' ');
        if (sp == string::npos) continue;
        int sender = stoi(reply.substr(0, sp));
        string body = reply.substr(sp + 1);
        if (sender <= 0 || sender >= agents || chunks_left[sender] <= 0) continue;
        --chunks_left[sender];
        --pending_chunks;
        for (int pos = 0; pos + 1 < (int)body.size(); pos += 2) {
            int bits = val95(body[pos]) + 95 * val95(body[pos + 1]);
            for (int b = 0; b < 13; ++b) {
                int logical = sender + bit_index[sender] * agents;
                int edge = logical < model.ecnt ? order[logical] : model.ecnt;
                ++bit_index[sender];
                if (edge >= model.ecnt) continue;
                if (bits & (1 << b)) model.add_edge(edge);
            }
        }
        if (claim_if_ready()) return;
    }

    if (claim_if_ready()) return;
    while (true) ask_line(".");
}

void coordinator_phased(int n, int agents, int id, int max_msg_len, const vector<int> &order, int block) {
    MazeModel model(n);
    vector<int> chunk_seen(agents, 0);

    auto claim_if_ready = [&]() -> bool {
        if (!model.connected()) return false;
        cout << "! " << model.build_path() << '\n' << flush;
        return true;
    };

    if (n == 1) {
        cout << "! " << '\n' << flush;
        return;
    }

    int my_total = slice_len(model.ecnt, agents, 0);
    int max_blocks = 0;
    for (int sender = 1; sender < agents; ++sender) {
        int q = slice_len(model.ecnt, agents, sender);
        max_blocks = max(max_blocks, (q + block - 1) / block);
    }
    max_blocks = max(max_blocks, (my_total + block - 1) / block);

    for (int blk = 0; blk < max_blocks; ++blk) {
        int start = blk * block;
        int take = min(block, max(0, my_total - start));
        for (int off = 0; off < take; ++off) {
            int logical = (start + off) * agents;
            int edge = order[logical];
            auto [r, c] = model.edge_cell(edge);
            string reply = ask_line("? " + to_string(r) + " " + to_string(c));
            if (!reply.empty() && reply[0] == '1') {
                model.add_edge(edge);
                if (claim_if_ready()) return;
            }
        }

        int pending = 0;
        for (int sender = 1; sender < agents; ++sender) {
            if (blk * block < slice_len(model.ecnt, agents, sender)) ++pending;
        }
        while (pending > 0) {
            string reply = ask_line("< ?");
            if (reply == "- -") continue;
            size_t sp = reply.find(' ');
            if (sp == string::npos) continue;
            int sender = stoi(reply.substr(0, sp));
            if (sender <= 0 || sender >= agents) continue;
            int msg_block = chunk_seen[sender]++;
            if (msg_block != blk) {
                // B is large enough that this should not happen, but keep decoding
                // by the actual sender sequence if scheduling ever gets ahead.
            }
            --pending;
            string body = reply.substr(sp + 1);
            int base = msg_block * block;
            int local = 0;
            for (int pos = 0; pos + 1 < (int)body.size(); pos += 2) {
                int bits = val95(body[pos]) + 95 * val95(body[pos + 1]);
                for (int b = 0; b < 13; ++b) {
                    int idx = base + local++;
                    if (idx >= slice_len(model.ecnt, agents, sender)) continue;
                    int logical = sender + idx * agents;
                    int edge = order[logical];
                    if (bits & (1 << b)) model.add_edge(edge);
                }
            }
            if (claim_if_ready()) return;
        }
    }

    if (claim_if_ready()) return;
    while (true) ask_line(".");
}

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

    int n, agents, id, max_msg_len;
    if (!(cin >> n >> agents >> id >> max_msg_len)) return 0;
    string rest;
    getline(cin, rest);

    MazeModel tmp(n);
    vector<int> order = build_edge_order(tmp);
    bool phased = agents >= 35 && n >= 101;
    int block = phased_block_size(tmp.ecnt, agents, max_msg_len);
    if (phased) {
        if (id == 0) coordinator_phased(n, agents, id, max_msg_len, order, block);
        else worker_phased(n, agents, id, max_msg_len, order, block);
    } else {
        if (id == 0) coordinator(n, agents, id, max_msg_len, order);
        else worker(n, agents, id, max_msg_len, order);
    }
    return 0;
}
