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

static inline string readLine() {
    string s;
    if (!std::getline(cin, s)) { return string(); }
    while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) s.pop_back();
    return s;
}
static inline void sendLine(const string& s) { cout << s << '\n'; cout.flush(); }

struct Edge { int r, c; int a, b; };

int main() {
    long long N, NUM_AGENTS, AGENT_ID, MAX_MSG_LEN;
    {
        string line = readLine();
        std::istringstream iss(line);
        iss >> N >> NUM_AGENTS >> AGENT_ID >> MAX_MSG_LEN;
    }
    long long m = (N + 1) / 2;

    // Build canonical edge list (identical on every agent).
    vector<Edge> edges;
    edges.reserve((size_t)max<long long>(0, 2 * m * (m - 1)));
    for (long long i = 0; i < m; ++i)
        for (long long j = 0; j < m; ++j) {
            int a = (int)(i * m + j);
            if (j + 1 < m) edges.push_back(Edge{(int)(2*i+1),(int)(2*j+2),a,(int)(i*m+(j+1))});
            if (i + 1 < m) edges.push_back(Edge{(int)(2*i+2),(int)(2*j+1),a,(int)((i+1)*m+j)});
        }
    long long E = (long long)edges.size();

    long long headerReserve = 24;
    long long chunkChars = MAX_MSG_LEN - headerReserve; if (chunkChars < 1) chunkChars = 1;
    long long chunkBits = chunkChars * 6;

    // ------- N==1 / no edges: trivial claim -------
    if (m <= 1 || E == 0) {
        if (AGENT_ID == 0) sendLine("! ");
        else sendLine("halt");
        return 0;
    }

    // Choose mode adaptively: coordinator+early-stop wins when it has enough
    // workers and the maze is big enough that streamed early-stop (~0.8*E scanned
    // in diagonal order, divided over W workers) beats full-map (E/NUM_AGENTS).
    long long W0 = NUM_AGENTS - 1;
    double coordEst = (W0 > 0) ? (0.8 * (double)E / (double)W0 + (double)W0 + 30.0) : 1e18;
    double fullEst  = (double)E / (double)NUM_AGENTS;
    bool useCoord = (NUM_AGENTS >= 12) && (coordEst < fullEst);

    // =====================================================================
    // FULL-MAP mode (small NUM_AGENTS): every agent queries a contiguous
    // block; workers stream packed bits to agent 0; agent 0 reconstructs.
    // =====================================================================
    if (!useCoord) {
        long long B = (E + NUM_AGENTS - 1) / NUM_AGENTS;
        long long lo = min(E, AGENT_ID * B);
        long long hi = min(E, lo + B);
        if (AGENT_ID != 0) {
            vector<char> bits((size_t)max<long long>(0, hi - lo), 0);
            for (long long e = lo; e < hi; ++e) {
                const Edge& ed = edges[(size_t)e];
                sendLine("? " + to_string(ed.r) + " " + to_string(ed.c));
                string rep = readLine();
                bits[(size_t)(e - lo)] = (!rep.empty() && rep[0]=='1') ? 1 : 0;
            }
            for (long long start = lo; start < hi; start += chunkBits) {
                long long cnt = min(chunkBits, hi - start);
                long long nchars = (cnt + 5) / 6; string packed; packed.reserve((size_t)nchars);
                for (long long ci = 0; ci < nchars; ++ci) {
                    int val = 0;
                    for (int k = 0; k < 6; ++k) { long long bidx = ci*6+k; if (bidx<cnt && bits[(size_t)((start-lo)+bidx)]) val|=(1<<k); }
                    packed.push_back((char)(32+val));
                }
                sendLine("> 0 " + to_string(start) + " " + to_string(cnt) + " " + packed);
                readLine();
            }
            sendLine("halt");
            return 0;
        }
        // agent 0
        vector<char> edgeEmpty((size_t)E, 0);
        long long filled = 0;
        for (long long e = lo; e < hi; ++e) {
            const Edge& ed = edges[(size_t)e];
            sendLine("? " + to_string(ed.r) + " " + to_string(ed.c));
            string rep = readLine();
            edgeEmpty[(size_t)e] = (!rep.empty() && rep[0]=='1') ? 1 : 0; ++filled;
        }
        while (filled < E) {
            sendLine("< ?");
            string rep = readLine();
            if (rep.empty() || rep[0]=='-') continue;
            size_t p1 = rep.find(' '); if (p1==string::npos) continue;
            string body = rep.substr(p1+1);
            size_t q1 = body.find(' '); if (q1==string::npos) continue;
            long long S = strtoll(body.substr(0,q1).c_str(),nullptr,10);
            size_t q2 = body.find(' ', q1+1); if (q2==string::npos) continue;
            long long cnt = strtoll(body.substr(q1+1,q2-(q1+1)).c_str(),nullptr,10);
            string packed = body.substr(q2+1);
            for (long long k=0;k<cnt;++k){ long long ci=k/6,kk=k%6; if((size_t)ci>=packed.size())break; int val=(int)(unsigned char)packed[(size_t)ci]-32; long long g=S+k; if(g>=0&&g<E) edgeEmpty[(size_t)g]=(char)((val>>kk)&1);} 
            filled += cnt;
        }
        // reconstruct
        long long total=m*m; vector<vector<int>> adj((size_t)total);
        for (long long e=0;e<E;++e) if(edgeEmpty[(size_t)e]){adj[(size_t)edges[(size_t)e].a].push_back(edges[(size_t)e].b);adj[(size_t)edges[(size_t)e].b].push_back(edges[(size_t)e].a);}
        vector<int> par((size_t)total,-2); int start=0,goal=(int)(total-1); par[(size_t)start]=-1;
        vector<int> q; q.reserve((size_t)total); q.push_back(start); size_t head=0;
        while(head<q.size()){int u=q[head++]; if(u==goal)break; for(int v:adj[(size_t)u]) if(par[(size_t)v]==-2){par[(size_t)v]=u;q.push_back(v);} }
        vector<int> pr; for(int cur=goal;cur!=-1;cur=par[(size_t)cur]){pr.push_back(cur); if(cur==start)break;} reverse(pr.begin(),pr.end());
        string moves; moves.reserve(pr.size()*2);
        for(size_t i=1;i<pr.size();++i){int d=pr[i]-pr[i-1]; if(d==1)moves+="RR"; else if(d==-1)moves+="LL"; else if(d==(int)m)moves+="DD"; else if(d==-(int)m)moves+="UU";}
        sendLine("! "+moves);
        return 0;
    }

    // =====================================================================
    // COORDINATOR mode (large NUM_AGENTS). Agent 0 = coordinator (reads +
    // incremental union-find, claims on S-T connect). Workers query the
    // potential edges in diagonal-priority order and stream carved bits.
    // =====================================================================
    // Build priority order (all agents compute identically).
    vector<int> order((size_t)E);
    for (long long e=0;e<E;++e) order[(size_t)e]=(int)e;
    long long cctr = 2*(m-1);
    auto keyDiag=[&](int e)->long long{ const Edge&ed=edges[(size_t)e]; /* room coords sums */
        // room a=(ai,aj), b: derive from a,b ids
        int ai=ed.a/(int)m, aj=ed.a%(int)m, bi=ed.b/(int)m, bj=ed.b%(int)m;
        long long mi2=ai+bi, mj2=aj+bj; long long d=llabs(mi2-mj2); long long along=llabs(mi2+mj2-cctr); return d*4000000LL+along*2000LL+e; };
    sort(order.begin(),order.end(),[&](int x,int y){return keyDiag(x)<keyDiag(y);});

    long long W = NUM_AGENTS - 1; // number of workers

    if (AGENT_ID != 0) {
        long long wi = AGENT_ID - 1; // 0..W-1
        long long g = W; if (g < 1) g = 1; // chunk size in bits
        // gather my positions: wi, wi+W, ...
        vector<char> mybits; mybits.reserve((size_t)(E/W + 2));
        long long k = 0; // index among my positions
        long long sentK = 0;
        for (long long pos = wi; pos < E; pos += W) {
            const Edge& ed = edges[(size_t)order[(size_t)pos]];
            sendLine("? " + to_string(ed.r) + " " + to_string(ed.c));
            string rep = readLine();
            mybits.push_back((!rep.empty() && rep[0]=='1') ? 1 : 0);
            ++k;
            if (k - sentK >= g) {
                long long cnt = k - sentK; long long nchars=(cnt+5)/6; string packed; packed.reserve((size_t)nchars);
                for (long long ci=0;ci<nchars;++ci){int val=0; for(int b=0;b<6;++b){long long bi=ci*6+b; if(bi<cnt && mybits[(size_t)(sentK+bi)]) val|=(1<<b);} packed.push_back((char)(32+val)); }
                sendLine("> 0 " + to_string(sentK) + " " + to_string(cnt) + " " + packed);
                readLine();
                sentK = k;
            }
        }
        if (k - sentK > 0) {
            long long cnt = k - sentK; long long nchars=(cnt+5)/6; string packed; packed.reserve((size_t)nchars);
            for (long long ci=0;ci<nchars;++ci){int val=0; for(int b=0;b<6;++b){long long bi=ci*6+b; if(bi<cnt && mybits[(size_t)(sentK+bi)]) val|=(1<<b);} packed.push_back((char)(32+val)); }
            sendLine("> 0 " + to_string(sentK) + " " + to_string(cnt) + " " + packed);
            readLine();
        }
        sendLine("halt");
        return 0;
    }

    // ---- Agent 0 coordinator ----
    long long total = m*m;
    // union-find
    vector<int> uf((size_t)total); for(long long i=0;i<total;++i) uf[(size_t)i]=(int)i;
    function<int(int)> find=[&](int x){ while(uf[(size_t)x]!=x){ uf[(size_t)x]=uf[(size_t)uf[(size_t)x]]; x=uf[(size_t)x];} return x; };
    vector<vector<int>> adj((size_t)total);
    int START=0, GOAL=(int)(total-1);
    bool connected=false;
    // guard against infinite wait: bound number of read attempts generously
    long long maxAttempts = 4*E + 100000;
    long long attempts=0;
    while (!connected) {
        if (++attempts > maxAttempts) break; // safety; shouldn't happen
        sendLine("< ?");
        string rep = readLine();
        if (rep.empty() || rep[0]=='-') continue;
        size_t p1 = rep.find(' '); if (p1==string::npos) continue;
        long long sender = strtoll(rep.substr(0,p1).c_str(),nullptr,10);
        long long wi = sender - 1;
        string body = rep.substr(p1+1);
        size_t q1 = body.find(' '); if (q1==string::npos) continue;
        long long k0 = strtoll(body.substr(0,q1).c_str(),nullptr,10);
        size_t q2 = body.find(' ', q1+1); if (q2==string::npos) continue;
        long long cnt = strtoll(body.substr(q1+1,q2-(q1+1)).c_str(),nullptr,10);
        string packed = body.substr(q2+1);
        for (long long t=0;t<cnt;++t){
            long long ci=t/6, kk=t%6; if((size_t)ci>=packed.size()) break;
            int val=(int)(unsigned char)packed[(size_t)ci]-32; int bit=(val>>kk)&1;
            long long kk2 = k0 + t; long long pos = wi + kk2*W;
            if (pos < 0 || pos >= E) continue;
            if (bit) {
                const Edge& ed = edges[(size_t)order[(size_t)pos]];
                adj[(size_t)ed.a].push_back(ed.b); adj[(size_t)ed.b].push_back(ed.a);
                int ra=find(ed.a), rb=find(ed.b); if(ra!=rb) uf[(size_t)ra]=rb;
            }
        }
        if (find(START)==find(GOAL)) connected=true;
    }
    // BFS over discovered carved edges to extract the path.
    vector<int> par((size_t)total,-2); par[(size_t)START]=-1;
    { vector<int> q; q.reserve((size_t)total); q.push_back(START); size_t head=0;
      while(head<q.size()){int u=q[head++]; if(u==GOAL)break; for(int v:adj[(size_t)u]) if(par[(size_t)v]==-2){par[(size_t)v]=u;q.push_back(v);} } }
    vector<int> pr; for(int cur=GOAL;cur!=-1;cur=par[(size_t)cur]){pr.push_back(cur); if(cur==START)break;} reverse(pr.begin(),pr.end());
    string moves; moves.reserve(pr.size()*2);
    for(size_t i=1;i<pr.size();++i){int d=pr[i]-pr[i-1]; if(d==1)moves+="RR"; else if(d==-1)moves+="LL"; else if(d==(int)m)moves+="DD"; else if(d==-(int)m)moves+="UU";}
    sendLine("! "+moves);
    return 0;
}
