// agent_v5 — dispatcher: v3 speculative for small A, cooperative EARLY-STOP mapper
// for larger A.
//
// Key idea (measured to beat full-map v2 across the real round distribution):
// full-map v2 probes all ~2T candidate passages (2T/A per agent). But we only need
// enough OPEN edges for the coordinator to see s=(0,0) connected to t=(m-1,m-1); it
// can then claim immediately. If every agent probes candidate passages in a shared
// DIAGONAL-FIRST order (by |i-j| of the edge's room), the s->t path is spanned after
// only X ~= 0.66-0.77 T open edges, so work ~ 2X/A << 2T/A. The coordinator claims
// the instant s and t union together, over the *real* discovered edges (a subforest
// of the maze tree -> the unique path).
//
//   * small A (<= SMALL_A):  v3 speculative — every agent races a distinct target-
//     biased DFS, fastest claims, zero messages, death-tolerant. (Best at small A.)
//   * larger A: cooperative early-stop. Agent 0 = coordinator (assembles + claims);
//     agents 1..A-1 = workers. Every agent owns the sorted-edge positions p with
//     p % A == id and probes them in diagonal-first order. Workers stream discovered
//     OPEN edges to the coordinator in packed messages; the coordinator also probes
//     its own share whenever its inbox is momentarily empty, unions every open edge
//     into a DSU, and claims as soon as find(s)==find(t). If workers die/stall it
//     falls back to probing all remaining edges itself, so it never fails to claim.
//
// Room model (merlin maze.rs): rooms at (2i+1,2j+1) always empty; only passage cells
// unknown. down edge idx--(idx+m) probes cell (2i+2,2j+1); right edge idx--(idx+1)
// probes (2i+1,2j+2). Each room step doubles into two ULDR moves.
#include <bits/stdc++.h>
using namespace std;

int N, NUM_AGENTS, AGENT_ID, MAX_MSG_LEN, m;
long long T;

static string readLine() {
    string s; int c;
    while ((c = getchar()) != EOF && c != '\n') s.push_back((char)c);
    if (c == EOF && s.empty()) exit(0);
    return s;
}
static inline bool ask(int r, int c) {
    printf("? %d %d\n", r, c); fflush(stdout);
    return readLine() == "1";
}
static inline void sendMsg(int dst, const string &body) {
    printf("> %d %s\n", dst, body.c_str()); fflush(stdout);
    readLine();  // "OK"
}
static inline bool recvAny(int &sender, string &body) {
    printf("< ?\n"); fflush(stdout);
    string r = readLine();
    if (r == "- -") return false;
    size_t sp = r.find(' ');
    sender = atoi(r.c_str());
    body = (sp == string::npos) ? "" : r.substr(sp + 1);
    return true;
}

// ---- dir tables: 0=Down(i+1) 1=Right(j+1) 2=Up(i-1) 3=Left(j-1) ----
static const int DI[4] = {1, 0, -1, 0};
static const int DJ[4] = {0, 1, 0, -1};
static const char DC[4] = {'D', 'R', 'U', 'L'};

// ============ v3 speculative DFS (small A) — adopted from agent.cpp ============
// kind 0 = fixed priority `prio`; kind 1/2 = ADAPTIVE, ordering the two toward-goal
// dirs first (longer / shorter remaining axis first), then the two away dirs.
static string dfs(int si, int sj, int gi, int gj, int kind, const int prio[4]) {
    vector<vector<char>> vis(m, vector<char>(m, 0));
    struct Frame { int i, j, next; };
    vector<Frame> st; string moves;
    vis[si][sj] = 1; st.push_back({si, sj, 0});
    int order[4];
    while (!st.empty()) {
        Frame &f = st.back();
        if (f.i == gi && f.j == gj) break;
        if (f.next == 4) { st.pop_back(); if (!moves.empty()) moves.pop_back(); continue; }
        if (kind == 0) { for (int t = 0; t < 4; ++t) order[t] = prio[t]; }
        else {
            int vdir = (gi > f.i) ? 0 : 2;
            int hdir = (gj > f.j) ? 1 : 3;
            int av = (vdir == 0) ? 2 : 0, ah = (hdir == 1) ? 3 : 1;
            bool longV = (abs(gi - f.i) >= abs(gj - f.j));
            if (kind == 2) longV = !longV;
            if (longV) { order[0] = vdir; order[1] = hdir; order[2] = av; order[3] = ah; }
            else       { order[0] = hdir; order[1] = vdir; order[2] = ah; order[3] = av; }
        }
        int d = order[f.next++];
        int ni = f.i + DI[d], nj = f.j + DJ[d];
        if (ni < 0 || ni >= m || nj < 0 || nj >= m || vis[ni][nj]) continue;
        int pr = (2 * f.i + 1) + DI[d], pc = (2 * f.j + 1) + DJ[d];
        if (!ask(pr, pc)) continue;
        vis[ni][nj] = 1; moves.push_back(DC[d]); st.push_back({ni, nj, 0});
    }
    return moves;
}
static void run_v3() {
    struct Strat { bool back; int kind; int prio[4]; };
    static const Strat V[8] = {
        {false, 1, {0,0,0,0}}, {true, 1, {0,0,0,0}}, {false, 2, {0,0,0,0}},
        {true, 0, {3,2,0,1}},  {false, 0, {1,0,2,3}}, {false, 0, {0,1,2,3}},
        {true, 0, {2,3,0,1}},  {false, 0, {0,1,3,2}},
    };
    const Strat &s = V[AGENT_ID % 8];
    int si = s.back ? m - 1 : 0, sj = si, gi = s.back ? 0 : m - 1, gj = gi;
    string rmoves = dfs(si, sj, gi, gj, s.kind, s.prio);
    string path; path.reserve(rmoves.size() * 2);
    if (!s.back) {
        for (char c : rmoves) { path.push_back(c); path.push_back(c); }
    } else {
        for (int k = (int)rmoves.size() - 1; k >= 0; --k) {
            char c = rmoves[k];
            char iv = (c=='D')?'U':(c=='U')?'D':(c=='R')?'L':'R';
            path.push_back(iv); path.push_back(iv);
        }
    }
    printf("! %s\n", path.c_str()); fflush(stdout);
}

// ================= cooperative early-stop (larger A) =================
// sorted candidate edges, diagonal-first. edge code = room*2 + dir (dir 0=down,1=right)
static vector<int> g_edges;   // codes, sorted by (|i-j|, room, dir)
static void build_edges() {
    g_edges.reserve(2 * T);
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < m; ++j) {
            int room = i * m + j;
            if (i + 1 < m) g_edges.push_back(room * 2 + 0);
            if (j + 1 < m) g_edges.push_back(room * 2 + 1);
        }
    stable_sort(g_edges.begin(), g_edges.end(), [](int a, int b) {
        int ra = a >> 1, rb = b >> 1;
        int ka = abs(ra / m - ra % m), kb = abs(rb / m - rb % m);
        if (ka != kb) return ka < kb;
        return a < b;
    });
}

// DSU
static vector<int> ufp;
static int uf(int x) { while (ufp[x] != x) { ufp[x] = ufp[ufp[x]]; x = ufp[x]; } return x; }
static inline bool un(int a, int b) { a = uf(a); b = uf(b); if (a == b) return false; ufp[a] = b; return true; }

static vector<uint8_t> openDown, openRight;   // coordinator's known-open edges
static inline void mark_open(int code) {
    int room = code >> 1, dir = code & 1;
    if (dir == 0) { openDown[room] = 1; un(room, room + m); }
    else          { openRight[room] = 1; un(room, room + 1); }
}
static inline bool probe_edge(int code) {      // probe one candidate; return open
    int room = code >> 1, dir = code & 1, i = room / m, j = room % m;
    int pr = (2 * i + 1) + DI[dir], pc = (2 * j + 1) + DJ[dir];
    bool op = ask(pr, pc);
    if (op) mark_open(code);
    return op;
}
static void claim_from_open() {
    // BFS s->t over known-open edges (a forest) -> unique path -> claim.
    vector<int> par(T, -1); vector<char> pmove(T, 0); vector<uint8_t> seen(T, 0);
    vector<int> q; q.reserve(T); q.push_back(0); seen[0] = 1;
    for (size_t h = 0; h < q.size(); ++h) {
        int idx = q[h];
        if (idx == (int)T - 1) break;
        int i = idx / m, j = idx % m;
        // Down/Up/Right/Left using open arrays
        if (i + 1 < m && openDown[idx] && !seen[idx + m])      { seen[idx+m]=1; par[idx+m]=idx; pmove[idx+m]='D'; q.push_back(idx+m); }
        if (i - 1 >= 0 && openDown[idx - m] && !seen[idx - m]) { seen[idx-m]=1; par[idx-m]=idx; pmove[idx-m]='U'; q.push_back(idx-m); }
        if (j + 1 < m && openRight[idx] && !seen[idx + 1])     { seen[idx+1]=1; par[idx+1]=idx; pmove[idx+1]='R'; q.push_back(idx+1); }
        if (j - 1 >= 0 && openRight[idx - 1] && !seen[idx - 1]){ seen[idx-1]=1; par[idx-1]=idx; pmove[idx-1]='L'; q.push_back(idx-1); }
    }
    string rmoves;
    for (int cur = (int)T - 1; cur != 0 && par[cur] != -1; cur = par[cur]) rmoves.push_back(pmove[cur]);
    reverse(rmoves.begin(), rmoves.end());
    string path; path.reserve(rmoves.size() * 2);
    for (char c : rmoves) { path.push_back(c); path.push_back(c); }
    printf("! %s\n", path.c_str()); fflush(stdout);
}

static void run_worker() {
    // A-1 workers (id 1..A-1) partition ALL edges; worker w=id-1 owns sorted
    // positions p with p % (A-1) == w, probed diagonal-first, streaming opens.
    const int W = NUM_AGENTS - 1, w = AGENT_ID - 1;
    const int CAP = max(8, min(36, (MAX_MSG_LEN - 4) / 7));  // edges per message
    string buf; int cnt = 0;
    size_t P = g_edges.size();
    for (size_t p = w; p < P; p += W) {
        int code = g_edges[p];
        int room = code >> 1, dir = code & 1, i = room / m, j = room % m;
        int pr = (2 * i + 1) + DI[dir], pc = (2 * j + 1) + DJ[dir];
        if (ask(pr, pc)) {
            if (cnt) buf.push_back(' ');
            buf += to_string(code); ++cnt;
            if (cnt >= CAP) { sendMsg(0, buf); buf.clear(); cnt = 0; }
        }
    }
    if (cnt) sendMsg(0, buf);
    printf("halt\n"); fflush(stdout);
}

static void run_coordinator() {
    openDown.assign(T, 0); openRight.assign(T, 0);
    const int S = 0, G = (int)(T - 1);
    size_t P = g_edges.size();
    // Pure receiver: drain workers' streamed opens, claim the instant s-t connects.
    int idle = 0;
    while (uf(S) != uf(G)) {
        int sender; string body;
        if (recvAny(sender, body)) {
            idle = 0;
            const char *s = body.c_str();
            while (*s) {
                if (*s < '0' || *s > '9') { ++s; continue; }
                int code = 0;
                while (*s >= '0' && *s <= '9') { code = code * 10 + (*s - '0'); ++s; }
                mark_open(code);
            }
            if (uf(S) == uf(G)) break;
        } else if (++idle > 400) {   // workers' first batch always arrives well before this
            // workers stalled/dead: probe everything ourselves so we never fail.
            for (size_t p = 0; p < P; ++p) {
                int c = g_edges[p], room = c >> 1, dir = c & 1;
                if ((dir == 0 && openDown[room]) || (dir == 1 && openRight[room])) continue;
                if (probe_edge(c) && uf(S) == uf(G)) break;
            }
            break;
        }
    }
    claim_from_open();
}

int main() {
    if (scanf("%d %d %d %d", &N, &NUM_AGENTS, &AGENT_ID, &MAX_MSG_LEN) != 4) return 0;
    { int ch; while ((ch = getchar()) != '\n' && ch != EOF) {} }
    m = (N + 1) / 2;
    if (m <= 1) {
        if (AGENT_ID == 0) printf("! \n"); else printf("halt\n");
        fflush(stdout); return 0;
    }
    T = (long long)m * m;

    const int SMALL_A = 5;   // A<=SMALL_A -> speculative; else cooperative early-stop
    if (NUM_AGENTS <= SMALL_A) { run_v3(); return 0; }

    ufp.resize(T); for (long long i = 0; i < T; ++i) ufp[i] = (int)i;
    build_edges();
    if (AGENT_ID == 0) run_coordinator();
    else run_worker();
    return 0;
}
