// v3: full-scan hybrid.
//
// The maze generator carves every (odd,odd) cell as a room; the only unknown
// bits are the 2*m*(m-1) edge cells between adjacent rooms. Probing ALL of
// them is embarrassingly parallel and needs no coordination: each scanner
// probes a fixed, globally-known slice of the edge list and streams result
// bits to the dispatcher, which maintains union-find over rooms and claims
// the unique path as soon as (1,1) and (N,N) become connected.
//
// Roles by AGENT_ID (fixed convention, no discovery):
//   A == 1      : agent 0 solo-walks (v1 DFS).
//   A <= 4      : every agent is an independent walker (start/goal sides,
//                 different tie-breaks), each claims directly. No messages.
//   A >= 5      : 0 = dispatcher, 1 = walker from start, 2 = walker from goal,
//                 3..A-1 = scanners (scanner q = ID-3 probes edge q, q+S, ...).
//                 First valid claim (walker or dispatcher) ends the run.
//
// Scanner -> dispatcher: "S <k> <enc>" — result bits for its edge sequence
// starting at sequence index k, packed 6 bits per char ('0'+v).
#include <bits/stdc++.h>
using namespace std;

static long long N, A, ID, MAXLEN;
static int m;

static const int di[4] = {1, 0, -1, 0}; // D R U L
static const int dj[4] = {0, 1, 0, -1};
static const char dch[4] = {'D', 'R', 'U', 'L'};

static string rdline() {
    string s;
    if (!getline(cin, s)) exit(0);
    return s;
}
static void wr(const string &s) { cout << s << "\n" << flush; }

// Global edge list: index -> (cell to probe, roomA, roomB). Same on all agents.
// Horizontal edges first (row-major), then vertical (row-major).
static long long numEdges() { return 2LL * m * (m - 1); }
static void edgeInfo(long long e, int &pr, int &pc, int &ra, int &rb) {
    long long H = (long long)m * (m - 1);
    if (e < H) { // (i,j)-(i,j+1)
        int i = (int)(e / (m - 1)), j = (int)(e % (m - 1));
        pr = 2 * i + 1; pc = 2 * j + 2;
        ra = i * m + j; rb = i * m + j + 1;
    } else { // (i,j)-(i+1,j)
        e -= H;
        int i = (int)(e / m), j = (int)(e % m);
        pr = 2 * i + 2; pc = 2 * j + 1;
        ra = i * m + j; rb = (i + 1) * m + j;
    }
}

// ---------------- walker: lazy goal-directed DFS, claims directly ----------
// fromStart: root (0,0) target (m-1,m-1), else reversed. bias flips D/R and
// U/L tie-breaks so two same-side walkers explore differently.
static void walker(bool fromStart, int bias) {
    int rootR = fromStart ? 0 : m - 1, rootC = rootR;
    int tgtR = fromStart ? m - 1 : 0, tgtC = tgtR;
    const int root = rootR * m + rootC, tgt = tgtR * m + tgtC;
    vector<uint8_t> vis((size_t)m * m, 0);
    vector<int> par((size_t)m * m, -1);
    vector<uint8_t> pmv((size_t)m * m, 0);
    struct Fr { int room; uint8_t ord[4]; int k; };
    vector<Fr> st;
    auto mkFr = [&](int room) {
        Fr f; f.room = room; f.k = 0;
        int r = room / m, c = room % m;
        int sc[4] = {tgtR - r, tgtC - c, r - tgtR, c - tgtC};
        int idx[4] = {0, 1, 2, 3};
        stable_sort(idx, idx + 4, [&](int a, int b) {
            if (sc[a] != sc[b]) return sc[a] > sc[b];
            return bias ? a > b : a < b;
        });
        for (int t = 0; t < 4; t++) f.ord[t] = (uint8_t)idx[t];
        return f;
    };
    vis[root] = 1;
    st.push_back(mkFr(root));
    while (!st.empty()) {
        Fr &f = st.back();
        if (f.k == 4) { st.pop_back(); continue; }
        int d = f.ord[f.k++];
        int i = f.room / m, j = f.room % m;
        int ni = i + di[d], nj = j + dj[d];
        if (ni < 0 || nj < 0 || ni >= m || nj >= m) continue;
        int nxt = ni * m + nj;
        if (vis[nxt]) continue;
        wr("? " + to_string(2 * i + 1 + di[d]) + " " + to_string(2 * j + 1 + dj[d]));
        if (rdline()[0] != '1') continue;
        vis[nxt] = 1;
        par[nxt] = f.room;
        pmv[nxt] = (uint8_t)d;
        if (nxt == tgt) {
            vector<uint8_t> seq; // moves root -> tgt
            for (int v = tgt; v != root; v = par[v]) seq.push_back(pmv[v]);
            reverse(seq.begin(), seq.end());
            string path;
            if (fromStart) {
                for (uint8_t mv : seq) { path += dch[mv]; path += dch[mv]; }
            } else { // walked goal->start; real path is the reverse, inverted
                for (int t = (int)seq.size() - 1; t >= 0; t--) {
                    char c = dch[seq[t] ^ 2];
                    path += c; path += c;
                }
            }
            wr("! " + path);
            exit(0);
        }
        st.push_back(mkFr(nxt));
    }
    // whole tree walked without reaching target: impossible unless bug
    wr("halt");
    exit(1);
}

// ---------------- solo fallback for the dispatcher ----------
static void soloSolve() { walker(true, 0); }

// ---------------- scanner ----------------
static void scanner(long long q, long long S) {
    const long long E = numEdges();
    long long batchBits = min(120LL, (MAXLEN - 24) * 6);
    if (batchBits < 6) batchBits = 6;
    string enc;
    long long done = 0, batchStart = 0;
    int acc = 0, accBits = 0;
    for (long long e = q; e < E; e += S) {
        int pr, pc, ra, rb;
        edgeInfo(e, pr, pc, ra, rb);
        wr("? " + to_string(pr) + " " + to_string(pc));
        int bit = rdline()[0] == '1' ? 1 : 0;
        acc = (acc << 1) | bit;
        if (++accBits == 6) { enc.push_back((char)('0' + acc)); acc = 0; accBits = 0; }
        done++;
        if (done - batchStart == batchBits) {
            wr("> 0 S " + to_string(batchStart) + " " + enc);
            rdline(); // OK
            enc.clear();
            batchStart = done;
        }
    }
    if (done > batchStart) {
        if (accBits) enc.push_back((char)('0' + (acc << (6 - accBits))));
        wr("> 0 S " + to_string(batchStart) + " " + enc);
        rdline();
    }
    wr("halt");
    exit(0);
}

// ---------------- dispatcher ----------------
struct Dsu {
    vector<int> p;
    Dsu(int n) : p(n) { iota(p.begin(), p.end(), 0); }
    int find(int x) { while (p[x] != x) x = p[x] = p[p[x]]; return x; }
    void unite(int a, int b) { p[find(a)] = find(b); }
};

// Walkers only pay off while the scan is slow: with W walkers the scan takes
// E/(A-1-W) turns, and a walker averages ~0.25*E turns with huge variance.
// Keep 2 walkers while scanners are few, none once the scan is clearly faster.
static int numWalkers() { return A <= 12 ? 2 : 0; }

static void dispatcher() {
    const long long E = numEdges();
    const long long S = A - 1 - numWalkers(); // scanners
    Dsu dsu(m * m);
    vector<uint8_t> open((size_t)m * m, 0); // per-room open-dir bitmask (D R U L)
    const int start = 0, goal = m * m - 1;
    long long got = 0;
    long long quiet = 0;

    auto tryClaim = [&]() {
        if (dsu.find(start) != dsu.find(goal)) return;
        vector<int> par((size_t)m * m, -1);
        vector<uint8_t> pmv((size_t)m * m, 0);
        deque<int> q{start};
        vector<uint8_t> seen((size_t)m * m, 0);
        seen[start] = 1;
        while (!q.empty()) {
            int cur = q.front(); q.pop_front();
            if (cur == goal) break;
            int r = cur / m, c = cur % m;
            for (int d = 0; d < 4; d++) {
                if (!(open[cur] & (1 << d))) continue;
                int nxt = (r + di[d]) * m + (c + dj[d]);
                if (seen[nxt]) continue;
                seen[nxt] = 1;
                par[nxt] = cur;
                pmv[nxt] = (uint8_t)d;
                q.push_back(nxt);
            }
        }
        if (!seen[goal]) return;
        string path;
        vector<uint8_t> seq;
        for (int v = goal; v != start; v = par[v]) seq.push_back(pmv[v]);
        for (int t = (int)seq.size() - 1; t >= 0; t--) {
            path += dch[seq[t]]; path += dch[seq[t]];
        }
        wr("! " + path);
        exit(0);
    };

    for (;;) {
        if (got >= E || quiet > 30000) soloSolve(); // safety net
        wr("< ?");
        string line = rdline();
        if (line == "- -") { quiet++; continue; }
        quiet = 0;
        // "<sender> S <k> <enc>"
        istringstream in(line);
        long long snd, k; string tag, enc;
        in >> snd >> tag >> k >> enc;
        if (tag != "S") continue;
        long long q = snd - 1 - numWalkers();
        for (size_t t = 0; t < enc.size() * 6; t++) {
            long long seqIdx = k + (long long)t;
            long long e = q + seqIdx * S;
            if (e >= E) break;
            int bit = ((enc[t / 6] - '0') >> (5 - t % 6)) & 1;
            got++;
            if (!bit) continue;
            int pr, pc, ra, rb;
            edgeInfo(e, pr, pc, ra, rb);
            dsu.unite(ra, rb);
            int d = (pr % 2 == 1) ? 1 : 0; // horizontal edge -> R from ra, else D
            open[ra] |= (1 << d);
            open[rb] |= (1 << (d ^ 2));
        }
        tryClaim();
    }
}

int main() {
    {
        istringstream in(rdline());
        in >> N >> A >> ID >> MAXLEN;
    }
    m = (int)((N + 1) / 2);
    if (N == 1) {
        if (ID == 0) wr("!");
        else wr("halt");
        return 0;
    }
    if (A == 1) { walker(true, 0); return 0; }
    if (A <= 4) {
        bool fromStart = (ID % 2 == 0);
        walker(fromStart, (int)(ID / 2));
        return 0;
    }
    int W = numWalkers();
    if (ID == 0) dispatcher();
    else if (ID <= W) walker(ID == 1, 0);
    else scanner(ID - 1 - W, A - 1 - W);
    return 0;
}
