// Datacenter Imprisonment — cooperative maze-escape agent.
//
// The empty cells form a tree carved by the provided generator, which ALWAYS
// makes rooms at (odd,odd) empty and pillars at (even,even) walls; only the
// "between" cells (exactly one even coordinate) are unknown tree edges. And
// `? r c` may probe ANY cell (agents are not physical robots). So instead of a
// slow single-agent search, all NUM_AGENTS agents cooperatively scan the edge
// set in parallel — agent a probes edges with index e s.t. e % K == a, one
// probe per lockstep turn. That makes the run take ~E/K turns instead of the
// tens of thousands a lone searcher needs (which timed out with many agents).
//
// Agent 0 is the assembler: it probes its own shard, gathers the other agents'
// packed results, rebuilds the full spanning tree, BFS-solves the unique
// room-graph path (0,0)->(m-1,m-1), and claims it. If a worker dies before
// delivering its shard, agent 0 self-probes the missing edges as a fallback, so
// a single dropout never stalls the run.

#include <bits/stdc++.h>
using namespace std;

static char linebuf[4096];

// Read one reply line from Merlin. Returns false on EOF (we were killed).
static bool readLine() {
    if (!fgets(linebuf, sizeof(linebuf), stdin)) return false;
    return true;
}

int main() {
    int N, K, ID, MAXMSG;
    if (scanf("%d %d %d %d", &N, &K, &ID, &MAXMSG) != 4) return 0;
    // Consume rest of the startup line.
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {}

    if (N == 1) { // (1,1) == (N,N): empty path.
        if (ID == 0) { printf("!\n"); fflush(stdout); }
        else { printf("halt\n"); fflush(stdout); }
        return 0;
    }

    const int m = (N + 1) / 2;                    // rooms per side
    const long long H = (long long)m * (m - 1);   // horizontal edges
    const long long E = 2 * H;                    // total potential tree edges

    // Cap message payload well under MAXMSG (leave margin for safety).
    const int CHUNK = max(1, min(240, MAXMSG - 4));
    const int BITS_PER_CHAR = 6;                  // pack 6 bits into printable chars 48..111

    auto edgeCell = [&](long long e, int &r, int &c) {
        if (e < H) { long long i = e / (m - 1), j = e % (m - 1); r = 2*i+1; c = 2*j+2; }
        else       { long long v = e - H; long long i = v / m, j = v % m; r = 2*i+2; c = 2*j+1; }
    };

    // Number of edges owned by agent a: e in [0,E) with e % K == a.
    auto ownedCount = [&](int a) -> long long {
        if (a >= E) return 0;
        return (E - 1 - a) / K + 1;
    };

    if (ID != 0) {
        // ---- WORKER ----
        long long cnt = ownedCount(ID);
        if (cnt == 0) { printf("halt\n"); fflush(stdout); return 0; }

        // Probe our shard, one per turn, accumulating result bits (LSB-first).
        string payload;
        int acc = 0, nbits = 0;
        for (long long t = 0; t < cnt; t++) {
            long long e = (long long)ID + t * K;
            int r, c; edgeCell(e, r, c);
            printf("? %d %d\n", r, c); fflush(stdout);
            if (!readLine()) return 0;
            int bit = (linebuf[0] == '1') ? 1 : 0;
            acc |= (bit << nbits);
            if (++nbits == BITS_PER_CHAR) { payload.push_back((char)(48 + acc)); acc = 0; nbits = 0; }
        }
        if (nbits > 0) payload.push_back((char)(48 + acc));

        // Send payload to agent 0 in chunks, one message per turn.
        for (size_t off = 0; off < payload.size(); off += CHUNK) {
            string piece = payload.substr(off, CHUNK);
            printf("> 0 %s\n", piece.c_str()); fflush(stdout);
            if (!readLine()) return 0;      // "OK"
        }
        printf("halt\n"); fflush(stdout);   // done; leave rotation
        return 0;
    }

    // ---- ASSEMBLER (agent 0) ----
    vector<char> present((size_t)E, 0);     // edge present flag
    vector<char> known((size_t)E, 0);       // resolved?
    long long knownCount = 0;

    auto setEdge = [&](long long e, int bit) {
        if (!known[e]) { known[e] = 1; present[e] = (char)bit; knownCount++; }
    };

    // Probe our own shard.
    {
        long long cnt = ownedCount(0);
        for (long long t = 0; t < cnt; t++) {
            long long e = t * K; // ID==0
            int r, c; edgeCell(e, r, c);
            printf("? %d %d\n", r, c); fflush(stdout);
            if (!readLine()) return 0;
            setEdge(e, (linebuf[0] == '1') ? 1 : 0);
        }
    }

    // Expected bit count from each sender and how many we've decoded so far.
    vector<long long> expect(K, 0), got(K, 0);
    for (int a = 1; a < K; a++) expect[a] = ownedCount(a);

    // Stuck detection: if a worker died, self-probe its missing shard instead.
    long long maxMsgs = 0;
    for (int a = 1; a < K; a++) {
        long long chars = (expect[a] + BITS_PER_CHAR - 1) / BITS_PER_CHAR;
        long long msgs = (chars + CHUNK - 1) / CHUNK;
        maxMsgs = max(maxMsgs, msgs);
    }
    const long long STUCK = 2 * maxMsgs + 5LL * K + 1000;

    long long consecutiveMiss = 0;
    while (knownCount < E && consecutiveMiss < STUCK) {
        printf("< ?\n"); fflush(stdout);
        if (!readLine()) return 0;
        // Parse "<sender> <body>" or "- -".
        if (linebuf[0] == '-') { consecutiveMiss++; continue; }
        consecutiveMiss = 0;
        // sender = leading integer, body = after first space.
        int sender = 0; char *p = linebuf;
        while (*p >= '0' && *p <= '9') { sender = sender * 10 + (*p - '0'); p++; }
        if (*p == ' ') p++;
        // Strip trailing newline/cr.
        char *end = p; while (*end && *end != '\n' && *end != '\r') end++;
        *end = '\0';
        if (sender <= 0 || sender >= K) continue;
        // Decode payload bits and place at edges sender + (got[sender]+i)*K.
        for (char *q = p; *q; q++) {
            int val = (int)(*q) - 48;
            for (int k = 0; k < BITS_PER_CHAR && got[sender] < expect[sender]; k++) {
                int bit = (val >> k) & 1;
                long long e = (long long)sender + got[sender] * K;
                setEdge(e, bit);
                got[sender]++;
            }
        }
    }

    // Fallback: probe any edges still unknown (a worker must have dropped).
    for (long long e = 0; e < E; e++) {
        if (known[e]) continue;
        int r, c; edgeCell(e, r, c);
        printf("? %d %d\n", r, c); fflush(stdout);
        if (!readLine()) return 0;
        setEdge(e, (linebuf[0] == '1') ? 1 : 0);
    }

    // Build room-graph adjacency from present edges.
    // rightOpen[i*m+j] : edge (i,j)-(i,j+1); downOpen[i*m+j] : edge (i,j)-(i+1,j).
    vector<char> rightOpen((size_t)m * m, 0), downOpen((size_t)m * m, 0);
    for (long long e = 0; e < E; e++) {
        if (!present[e]) continue;
        if (e < H) { long long i = e / (m - 1), j = e % (m - 1); rightOpen[i * m + j] = 1; }
        else       { long long v = e - H; long long i = v / m, j = v % m; downOpen[i * m + j] = 1; }
    }

    // BFS the unique path (0,0) -> (m-1,m-1) in the room graph.
    const int total = m * m;
    vector<int> par(total, -1);
    vector<char> pdir(total, 0);           // direction from parent to this room
    vector<char> vis(total, 0);
    vector<int> q; q.reserve(total);
    int startId = 0, goalId = (m - 1) * m + (m - 1);
    vis[startId] = 1; q.push_back(startId);
    for (size_t head = 0; head < q.size(); head++) {
        int cur = q[head];
        if (cur == goalId) break;
        int i = cur / m, j = cur % m;
        if (j + 1 < m && rightOpen[cur] && !vis[i * m + (j + 1)]) {
            int nx = i * m + (j + 1); vis[nx] = 1; par[nx] = cur; pdir[nx] = 'R'; q.push_back(nx);
        }
        if (j > 0 && rightOpen[i * m + (j - 1)] && !vis[i * m + (j - 1)]) {
            int nx = i * m + (j - 1); vis[nx] = 1; par[nx] = cur; pdir[nx] = 'L'; q.push_back(nx);
        }
        if (i + 1 < m && downOpen[cur] && !vis[(i + 1) * m + j]) {
            int nx = (i + 1) * m + j; vis[nx] = 1; par[nx] = cur; pdir[nx] = 'D'; q.push_back(nx);
        }
        if (i > 0 && downOpen[(i - 1) * m + j] && !vis[(i - 1) * m + j]) {
            int nx = (i - 1) * m + j; vis[nx] = 1; par[nx] = cur; pdir[nx] = 'U'; q.push_back(nx);
        }
    }

    // Reconstruct room-step directions goal->start, then reverse.
    string dirs;
    for (int cur = goalId; cur != startId; cur = par[cur]) {
        if (par[cur] < 0) { dirs.clear(); break; } // safety: unreachable
        dirs.push_back(pdir[cur]);
    }
    reverse(dirs.begin(), dirs.end());

    // Each room step spans two cells -> emit the move twice.
    string moves;
    moves.reserve(dirs.size() * 2);
    for (char d : dirs) { moves.push_back(d); moves.push_back(d); }

    printf("! %s\n", moves.c_str());
    fflush(stdout);
    return 0;
}
