// Problem D: Datacenter Imprisonment
//
// Progressive parallel solver.
// Candidate room-wall edges are queried in a geometry-prioritized order.  In
// each phase every agent probes a fixed block of positions from that order.
// Non-leaders send a bitmap of their open/closed answers to agent 0; because the
// schedule is deterministic, the bitmap is enough to identify every open edge.
// Agent 0 maintains the known-open graph and claims as soon as the two corners
// become connected.  If the priority order is unlucky this degrades to a full
// exact map, so correctness does not depend on the heuristic.

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

struct Edge {
    int u;
    int v;
    int wr;
    int wc;
    char dir; // direction from u to v in room space
};

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); }
};

static int N, NUM_AGENTS, AGENT_ID, MAX_MSG_LEN;
static const string ALPH =
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_~";

static int blockSize() {
    return (NUM_AGENTS <= 5 ? 512 : 320);
}

static string readLine() {
    string s;
    if (!getline(cin, s)) return string();
    return s;
}

static bool queryEmpty(int r, int c) {
    cout << "? " << r << ' ' << c << '\n' << flush;
    string reply = readLine();
    return !reply.empty() && reply[0] == '1';
}

static void sendMsg(int dst, const string &body) {
    cout << "> " << dst << ' ' << body << '\n' << flush;
    (void)readLine(); // OK
}

static string recvAny() {
    cout << "< ?\n" << flush;
    return readLine();
}

static void skipTurn() {
    cout << ".\n" << flush;
    (void)readLine(); // OK
}

static void haltNow() {
    cout << "halt\n" << flush;
}

static char oppositeDir(char c) {
    if (c == 'D') return 'U';
    if (c == 'U') return 'D';
    if (c == 'R') return 'L';
    return 'R';
}

static vector<Edge> buildEdges(int m) {
    vector<Edge> edges;
    edges.reserve(2 * m * max(0, m - 1));
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < m; ++j) {
            int u = i * m + j;
            int gr = 2 * i + 1;
            int gc = 2 * j + 1;
            if (i + 1 < m) {
                int v = (i + 1) * m + j;
                edges.push_back({u, v, gr + 1, gc, 'D'});
            }
            if (j + 1 < m) {
                int v = i * m + (j + 1);
                edges.push_back({u, v, gr, gc + 1, 'R'});
            }
        }
    }
    return edges;
}

static vector<int> buildPriorityOrder(const vector<Edge> &edges, int m) {
    // Edge midpoint coordinates are represented doubled:
    // x2 = i1+i2, y2 = j1+j2.  The score is approximately
    //   distance_to_main_diagonal + 0.1 * distance_to_anti_diagonal_midpoint.
    // It prioritizes a broad diagonal corridor while still being a total order.
    int maxScore = 24 * max(1, m) + 32;
    vector<vector<int>> buckets(maxScore);
    for (int id = 0; id < (int)edges.size(); ++id) {
        int u = edges[id].u, v = edges[id].v;
        int i1 = u / m, j1 = u % m;
        int i2 = v / m, j2 = v % m;
        int x2 = i1 + i2;
        int y2 = j1 + j2;
        int diag2 = abs(x2 - y2);
        int anti2 = abs(x2 + y2 - 2 * (m - 1));
        int score = 10 * diag2 + anti2;
        if (score >= (int)buckets.size()) buckets.resize(score + 1);
        buckets[score].push_back(id);
    }
    vector<int> order;
    order.reserve(edges.size());
    for (auto &b : buckets) {
        for (int id : b) order.push_back(id);
    }
    return order;
}

static string encodeBitmap(const vector<unsigned char> &bits, int block) {
    string body = "B";
    int chars = (block + 5) / 6;
    body.reserve(1 + chars);
    for (int c = 0; c < chars; ++c) {
        int val = 0;
        for (int b = 0; b < 6; ++b) {
            int t = c * 6 + b;
            if (t < block && bits[t]) val |= (1 << b);
        }
        body.push_back(ALPH[val]);
    }
    return body;
}

static void addOpenEdge(vector<array<int, 4>> &nbr,
                        vector<array<char, 4>> &ndir,
                        vector<unsigned char> &deg,
                        DSU &dsu,
                        const Edge &e) {
    int du = deg[e.u]++;
    nbr[e.u][du] = e.v;
    ndir[e.u][du] = e.dir;
    int dv = deg[e.v]++;
    nbr[e.v][dv] = e.u;
    ndir[e.v][dv] = oppositeDir(e.dir);
    dsu.unite(e.u, e.v);
}

static string buildClaimPath(const vector<array<int, 4>> &nbr,
                             const vector<array<char, 4>> &ndir,
                             const vector<unsigned char> &deg,
                             int V) {
    vector<int> parent(V, -1);
    vector<char> pdir(V, 0);
    queue<int> q;
    q.push(0);
    parent[0] = 0;
    while (!q.empty() && parent[V - 1] == -1) {
        int u = q.front();
        q.pop();
        for (int k = 0; k < deg[u]; ++k) {
            int v = nbr[u][k];
            if (parent[v] != -1) continue;
            parent[v] = u;
            pdir[v] = ndir[u][k];
            q.push(v);
            if (v == V - 1) break;
        }
    }

    string path;
    if (V > 1) {
        vector<char> dirs;
        int cur = V - 1;
        while (cur != 0 && cur != -1) {
            dirs.push_back(pdir[cur]);
            cur = parent[cur];
        }
        reverse(dirs.begin(), dirs.end());
        path.reserve(2 * dirs.size());
        for (char c : dirs) {
            path.push_back(c);
            path.push_back(c);
        }
    }
    return path;
}

static void claimNow(const vector<array<int, 4>> &nbr,
                     const vector<array<char, 4>> &ndir,
                     const vector<unsigned char> &deg,
                     int V) {
    string path = buildClaimPath(nbr, ndir, deg, V);
    cout << "! " << path << '\n' << flush;
}

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

    {
        string line = readLine();
        istringstream in(line);
        in >> N >> NUM_AGENTS >> AGENT_ID >> MAX_MSG_LEN;
    }

    int m = (N + 1) / 2;
    int V = m * m;
    vector<Edge> edges = buildEdges(m);
    vector<int> order = buildPriorityOrder(edges, m);
    int E = (int)order.size();
    int block = blockSize();
    int phases = (E + block * NUM_AGENTS - 1) / (block * NUM_AGENTS);

    vector<array<int, 4>> nbr;
    vector<array<char, 4>> ndir;
    vector<unsigned char> deg;
    DSU dsu;
    if (AGENT_ID == 0) {
        nbr.resize(V);
        ndir.resize(V);
        deg.assign(V, 0);
        dsu.init(V);
    }

    for (int phase = 0; phase < phases; ++phase) {
        vector<unsigned char> bits(block, 0);
        int phaseBase = phase * block * NUM_AGENTS;
        for (int t = 0; t < block; ++t) {
            int pos = phaseBase + t * NUM_AGENTS + AGENT_ID;
            if (pos >= E) break;
            int eid = order[pos];
            const Edge &e = edges[eid];
            if (queryEmpty(e.wr, e.wc)) {
                bits[t] = 1;
                if (AGENT_ID == 0) addOpenEdge(nbr, ndir, deg, dsu, e);
            }
        }

        if (AGENT_ID == 0) {
            if (dsu.same(0, V - 1)) {
                claimNow(nbr, ndir, deg, V);
                return 0;
            }
            if (NUM_AGENTS > 1) skipTurn();
            for (int got = 0; got < NUM_AGENTS - 1; ) {
                string line = recvAny();
                if (line == "- -" || line.empty()) continue;
                size_t sp = line.find(' ');
                if (sp == string::npos) continue;
                int sender = stoi(line.substr(0, sp));
                string body = line.substr(sp + 1);
                if (sender <= 0 || sender >= NUM_AGENTS || body.empty() || body[0] != 'B') {
                    continue;
                }
                ++got;
                for (int c = 0; c + 1 < (int)body.size(); ++c) {
                    int val = (int)ALPH.find(body[c + 1]);
                    if (val < 0) continue;
                    for (int b = 0; b < 6; ++b) {
                        if ((val & (1 << b)) == 0) continue;
                        int t = c * 6 + b;
                        if (t >= block) continue;
                        int pos = phaseBase + t * NUM_AGENTS + sender;
                        if (pos >= E) continue;
                        addOpenEdge(nbr, ndir, deg, dsu, edges[order[pos]]);
                    }
                }
                if (dsu.same(0, V - 1)) {
                    claimNow(nbr, ndir, deg, V);
                    return 0;
                }
            }
        } else {
            sendMsg(0, encodeBitmap(bits, block));
            for (int i = 0; i < NUM_AGENTS - 1; ++i) skipTurn();
        }
    }

    if (AGENT_ID == 0) {
        claimNow(nbr, ndir, deg, V);
    } else {
        haltNow();
    }
    return 0;
}
