#pragma GCC optimize("Ofast,unroll-loops,omit-frame-pointer")
#include <bits/stdc++.h>
using namespace std;

static int N, K, ID, MSGLEN, m, E, startRoom, goalRoom, g_token = 0;
static vector<int> eA, eB, g_dist, g_pe, g_pr, g_vis, g_wp;
static vector<vector<pair<int,int>>> adjE;

#ifndef P_NUM
#define P_NUM 344
#endif
#ifndef P_DEN
#define P_DEN 100
#endif
#ifndef P_CAP_SMALL
#define P_CAP_SMALL 32
#endif
#ifndef P_CAP_MED
#define P_CAP_MED 48
#endif
#ifndef P_CAP_BIG
#define P_CAP_BIG 80
#endif
#ifndef WP_MODE
#define WP_MODE 4
#endif
#ifndef WP_NUM
#define WP_NUM 210
#endif
#ifndef ROUTE_MODE
#define ROUTE_MODE 3
#endif
#ifndef EXTRA_DIRECT
#define EXTRA_DIRECT 8
#endif
#ifndef ASSIGN_MODE
#define ASSIGN_MODE 0
#endif
#ifndef BUCKET_SORT
#define BUCKET_SORT 0
#endif
#ifndef ADAPT_P
#define ADAPT_P 9
#endif
#ifndef WP_POS_MODE
#define WP_POS_MODE 0
#endif
#ifndef EDGE_MODE
#define EDGE_MODE 0
#endif
#ifndef ROUTE_TIE_MODE
#define ROUTE_TIE_MODE 0
#endif

static void buildEdges(){
    m = (N + 1) / 2;
    eA.clear(); eB.clear();
    adjE.assign(m*m, {});
    for(int i=0; i<m; i++) for(int j=0; j<m; j++){
        int u = i*m+j;
#if EDGE_MODE == 1
        if(i+1<m){ eA.push_back(u); eB.push_back(u+m); }
        if(j+1<m){ eA.push_back(u); eB.push_back(u+1); }
#else
        if(j+1<m){ eA.push_back(u); eB.push_back(u+1); }
        if(i+1<m){ eA.push_back(u); eB.push_back(u+m); }
#endif
    }
    E = (int)eA.size();
    for(int e=0; e<E; e++){ adjE[eA[e]].push_back({e,eB[e]}); adjE[eB[e]].push_back({e,eA[e]}); }
    startRoom = 0; goalRoom = m*m-1;
    g_vis.assign(m*m, 0); g_dist.resize(m*m); g_pe.resize(m*m); g_pr.resize(m*m);
}

struct View {
    vector<char> st;
    vector<int> p;
    void init(){ st.assign(E,0); p.resize(m*m); iota(p.begin(),p.end(),0); }
    int find(int x){ while(p[x]!=x){ p[x]=p[p[x]]; x=p[x]; } return x; }
    void uni(int a,int b){ a=find(a); b=find(b); if(a!=b) p[a]=b; }
    bool connected(){ return find(startRoom)==find(goalRoom); }
    void deduce(){
        static vector<int> cnt, last;
        if((int)cnt.size() != m*m){
            cnt.resize(m*m);
            last.resize(m*m);
        }
        bool ch = true;
        while(ch){
            ch = false;
            fill(cnt.begin(),cnt.end(),0);
            for(int e=0; e<E; e++) if(!st[e]){
                int ca=find(eA[e]), cb=find(eB[e]);
                if(ca==cb){ st[e]=2; ch=true; }
                else { cnt[ca]++; last[ca]=e; cnt[cb]++; last[cb]=e; }
            }
            for(int r=0; r<m*m; r++) if(cnt[r]==1){
                int e=last[r];
                if(!st[e]){ st[e]=1; uni(eA[e],eB[e]); ch=true; }
            }
        }
    }
    void feed(int e, bool open){
        if(st[e]) return;
        st[e] = open ? 1 : 2;
        if(open) uni(eA[e],eB[e]);
        deduce();
    }
    void feedRaw(int e, bool open){
        if(st[e]) return;
        st[e] = open ? 1 : 2;
        if(open) uni(eA[e],eB[e]);
    }
};

static View W;

static void say(const string& s){ cout << s << endl; }
static string hear(){
    string s;
    if(!getline(cin, s)) _exit(0);
    while(!s.empty() && (s.back()=='\r' || s.back()=='\n')) s.pop_back();
    return s;
}

[[noreturn]] static void claimNow(){
    vector<int> par(m*m, -2); par[startRoom]=-1;
    queue<int> q; q.push(startRoom);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(auto& prm : adjE[u]){
            int e=prm.first, w=prm.second;
            if(W.st[e]==1 && par[w]==-2){ par[w]=u; q.push(w); }
        }
    }
    vector<int> path;
    for(int cur=goalRoom; cur!=-1; cur=par[cur]) path.push_back(cur);
    reverse(path.begin(), path.end());
    string moves;
    for(size_t t=1; t<path.size(); t++){
        int a=path[t-1], b=path[t];
        char mv = (b/m) > (a/m) ? 'D' : (b/m) < (a/m) ? 'U' : (b%m) > (a%m) ? 'R' : 'L';
        moves += mv; moves += mv;
    }
    say("! " + moves);
    _exit(0);
}

static void feedW(int e, bool open) {
    W.feed(e, open);
    if(W.connected()) claimNow();
}
static void finishWBatch() {
    W.deduce();
    if(W.connected()) claimNow();
}

static int activeWaypointLinks(View& v) {
    vector<int> cwp;
    for(int x : g_wp) if(cwp.empty() || v.find(cwp.back()) != v.find(x)) cwp.push_back(x);
    set<pair<int,int>> seen;
    int cnt = 0;
    for(size_t i=0; i+1<cwp.size(); i++){
        int ca = v.find(cwp[i]), cb = v.find(cwp[i+1]);
        if(ca == cb) continue;
        auto key = minmax(ca, cb);
        if(seen.insert(key).second) cnt++;
    }
    return cnt;
}

static vector<int> routeDijkstra(View& v, int srcRoom, int dstRoom, int actorId){
    int dstC = v.find(dstRoom), srcC = v.find(srcRoom), found = -1;
    g_token++;
#if ROUTE_MODE == 1
    queue<int> q;
    for(int u=0; u<m*m; u++) if(v.find(u) == srcC){
        g_vis[u] = g_token; g_pe[u] = g_pr[u] = -1;
        q.push(u);
    }
    while(!q.empty()){
        int u = q.front(); q.pop();
        if(v.find(u) == dstC){ found = u; break; }
        for(auto& prm : adjE[u]){
            int e = prm.first, w = prm.second;
            if(v.st[e] == 2 || g_vis[w] == g_token) continue;
            g_vis[w] = g_token; g_pe[w] = e; g_pr[w] = u;
            q.push(w);
        }
    }
#elif ROUTE_MODE == 2
    deque<int> dq;
    for(int u=0; u<m*m; u++) if(v.find(u) == srcC){
        g_vis[u] = g_token; g_dist[u] = 0; g_pe[u] = g_pr[u] = -1;
        dq.push_back(u);
    }
    while(!dq.empty()){
        int u = dq.front(); dq.pop_front();
        if(v.find(u) == dstC){ found = u; break; }
        int du = g_dist[u];
        for(auto& prm : adjE[u]){
            int e = prm.first, w = prm.second;
            if(v.st[e] == 2) continue;
            int nd = du + (v.st[e] == 1 ? 0 : 1);
            if(g_vis[w] != g_token || nd < g_dist[w]){
                g_vis[w] = g_token; g_dist[w] = nd; g_pe[w] = e; g_pr[w] = u;
                if(v.st[e] == 1) dq.push_front(w); else dq.push_back(w);
            }
        }
    }
#elif ROUTE_MODE == 3
    static vector<vector<int>> bucket;
    static vector<int> bmark;
    static int btoken = 1;
    int maxD = 3 * m * m + 10;
    if((int)bucket.size() < maxD){
        bucket.assign(maxD, {});
        bmark.assign(maxD, 0);
        btoken = 1;
    }
    if(++btoken == INT_MAX){
        fill(bmark.begin(), bmark.end(), 0);
        btoken = 1;
    }
    auto push_bucket = [&](int d, int u){
        if(d >= maxD) d = maxD - 1;
        if(bmark[d] != btoken){
            bmark[d] = btoken;
            bucket[d].clear();
        }
        bucket[d].push_back(u);
    };
    for(int u=0; u<m*m; u++) if(v.find(u) == srcC){
        g_vis[u] = g_token; g_dist[u] = 0; g_pe[u] = g_pr[u] = -1;
        push_bucket(0, u);
    }
    for(int dcur=0; dcur<maxD && found<0; dcur++){
        if(bmark[dcur] != btoken) continue;
        auto& b = bucket[dcur];
#if BUCKET_SORT
        sort(b.begin(), b.end());
#endif
        for(int qi=0; qi<(int)b.size(); qi++){
            int u = b[qi];
            if(g_vis[u] != g_token || g_dist[u] != dcur) continue;
            if(v.find(u) == dstC){ found = u; break; }
            auto relax = [&](const pair<int,int>& prm){
                int e = prm.first, w = prm.second;
                if(v.st[e] == 2) return;
                int nd = dcur + (v.st[e] == 1 ? 2 : 3);
                if(g_vis[w] != g_token || nd < g_dist[w]){
                    g_vis[w] = g_token; g_dist[w] = nd; g_pe[w] = e; g_pr[w] = u;
                    push_bucket(nd, w);
                }
            };
#if ROUTE_TIE_MODE == 1
            if(actorId & 1) for(auto it=adjE[u].rbegin(); it!=adjE[u].rend(); ++it) relax(*it);
            else for(auto& prm : adjE[u]) relax(prm);
#else
            for(auto& prm : adjE[u]) relax(prm);
#endif
        }
    }
#else
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    for(int u=0; u<m*m; u++) if(v.find(u) == srcC){
        g_vis[u] = g_token; g_dist[u] = 0; g_pe[u] = g_pr[u] = -1;
        pq.push({0, u});
    }
    while(!pq.empty()){
        auto [d, u] = pq.top(); pq.pop();
        if(g_vis[u] != g_token || d > g_dist[u]) continue;
        if(v.find(u) == dstC){ found = u; break; }
        for(auto& prm : adjE[u]){
            int e = prm.first, w = prm.second;
            if(v.st[e] == 2) continue;
            int next_d = d + (v.st[e] == 1 ? 100 : 150);
            if(g_vis[w] != g_token || next_d < g_dist[w]){
                g_vis[w] = g_token; g_dist[w] = next_d; g_pe[w] = e; g_pr[w] = u;
                pq.push({next_d, w});
            }
        }
    }
#endif
    vector<int> res;
    if(found < 0) return res;
    for(int u=found; g_pr[u]>=0; u=g_pr[u]) if(v.st[g_pe[u]] == 0) res.push_back(g_pe[u]);
    reverse(res.begin(), res.end());
    return res;
}

struct Actor {
    View* v;
    int aid, cursor = 0, ranksPerSlot = 1;
    struct Slot { int src, dst, rank; };
    vector<Slot> slots;

    void init(View* view, int id){
        v = view; aid = id; cursor = 0; slots.clear();
        vector<int> cwp;
        for(int x : g_wp) if(cwp.empty() || v->find(cwp.back())!=v->find(x)) cwp.push_back(x);
        vector<pair<int,int>> links;
        set<pair<int,int>> seen;
        for(size_t i=0; i+1<cwp.size(); i++){
            int ca=v->find(cwp[i]), cb=v->find(cwp[i+1]);
            if(ca==cb) continue;
            auto key = minmax(ca,cb);
            if(seen.count(key)) continue;
            seen.insert(key);
            links.push_back({cwp[i], cwp[i+1]});
        }
#if EXTRA_DIRECT == 1
        if(v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 2
        if(v->find(startRoom) != v->find(goalRoom)) links.push_back({startRoom, goalRoom});
#elif EXTRA_DIRECT == 3
        if(K == 12 && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 4
        if((K == 10 || K == 12) && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 5
        if(K == 12 && v->find(startRoom) != v->find(goalRoom)) links.push_back({startRoom, goalRoom});
#elif EXTRA_DIRECT == 6
        if(K == 16 && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 7
        if((K == 12 || K == 16) && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 8
        if((K == 12 || (K == 16 && N >= 151)) && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#elif EXTRA_DIRECT == 9
        if(K == 12 && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
        else if(K == 16 && N >= 151 && v->find(startRoom) != v->find(goalRoom)) links.push_back({startRoom, goalRoom});
#elif EXTRA_DIRECT == 10
        if(K == 12 && v->find(startRoom) != v->find(goalRoom)) {
            links.insert(links.begin(), {startRoom, goalRoom});
            links.push_back({startRoom, goalRoom});
        } else if(K == 16 && N >= 151 && v->find(startRoom) != v->find(goalRoom)) links.insert(links.begin(), {startRoom, goalRoom});
#endif
        int S = (int)links.size()*2;
        if(S==0) return;
        if(S>=K){
            for(int s=aid; s<S; s+=K){
                auto& lnk = links[s/2];
                int d = s%2;
                slots.push_back({d?lnk.second:lnk.first, d?lnk.first:lnk.second, 0});
            }
        } else {
            ranksPerSlot = (K+S-1)/S;
#if ASSIGN_MODE == 1 || ASSIGN_MODE == 2
            bool spreadAssign = (ASSIGN_MODE == 1) || (K >= 10 && K < 16);
            if(spreadAssign){
            int s = (int)((long long)aid * S / K);
            int rank = 0;
            for(int x=0; x<aid; x++) if((int)((long long)x * S / K) == s) rank++;
            int d=s%2;
            auto& lnk = links[s/2];
            slots.push_back({d?lnk.second:lnk.first, d?lnk.first:lnk.second, rank});
            } else {
            int s=aid%S, d=s%2;
            auto& lnk = links[s/2];
            slots.push_back({d?lnk.second:lnk.first, d?lnk.first:lnk.second, aid/S});
            }
#else
            int s=aid%S, d=s%2;
            auto& lnk = links[s/2];
            slots.push_back({d?lnk.second:lnk.first, d?lnk.first:lnk.second, aid/S});
#endif
        }
    }
    static int pickPos(int rank, int nranks, int len){
        int sp = max(1, len * 3 / 4);
        int idx = rank * sp / nranks;
        return idx >= sp ? 0 : idx;
    }
    int nextProbe(){
        int S=(int)slots.size();
        for(int t=0; t<S; t++){
            Slot& s = slots[(cursor+t)%S];
            if(v->find(s.src)==v->find(s.dst)) continue;
            vector<int> r = routeDijkstra(*v, s.src, s.dst, aid);
            if(r.empty()) continue;
            cursor = (cursor+t+1)%S;
            return r[pickPos(s.rank, ranksPerSlot, (int)r.size())];
        }
        if(v->connected()) return -1;
        int src = (aid%2)? goalRoom : startRoom, dst = goalRoom - src;
        vector<int> r = routeDijkstra(*v, src, dst, aid);
        return r.empty() ? -1 : r[pickPos(aid/2, max(1,K/2), (int)r.size())];
    }
    void feed(int e, bool open){ v->feed(e, open); }
};

static string encodeBits(const vector<uint8_t>& bits){
    string s;
    int acc=0, nb=0;
    for(uint8_t b : bits){
        acc |= (b&1)<<nb;
        if(++nb==6){ s += (48+acc); acc=nb=0; }
    }
    if(nb) s += (48+acc);
    return s;
}
static vector<uint8_t> decodeBits(const string& s, int want){
    vector<uint8_t> bits;
    for(char c : s){
        int v = c-48;
        for(int b=0; b<6 && (int)bits.size()<want; b++) bits.push_back((v>>b)&1);
    }
    bits.resize(want, 0);
    return bits;
}

int main(){
    {
        string l = hear();
        sscanf(l.c_str(), "%d %d %d %d", &N, &K, &ID, &MSGLEN);
    }
    buildEdges();
    if(m<=1){ say("! "); _exit(0); }

    int B0 = max(2, min(K, m));
#if WP_MODE == 1
    B0 = max(2, min(m, (K + 1) / 2));
#elif WP_MODE == 2
    B0 = 2;
#elif WP_MODE == 3
    B0 = max(2, min(m, (int)llround(sqrt((double)K) * 2.0)));
#elif WP_MODE == 4
    B0 = max(2, min(m, (int)llround((WP_NUM / 100.0) * sqrt((double)K))));
#endif
    for(int b=0; b<B0; b++){
#if WP_POS_MODE == 1
        int pos = (int)((long long)b * (m-1) / (B0-1));
#elif WP_POS_MODE == 2
        int pos = (int)(((long long)(b+1) * (m-1)) / B0);
        if(b == 0) pos = 0;
        if(b == B0-1) pos = m-1;
#else
        int pos = (int)(((long long)2*b*(m-1) + (B0-1)) / (2*(B0-1)));
#endif
        g_wp.push_back(pos*m + pos);
    }

    int d=0; while((1<<d)<K) d++;
    int cap = (K >= 8 ? P_CAP_BIG : K >= 4 ? P_CAP_MED : P_CAP_SMALL);
    int baseP = max(8, min(cap, (int)llround((P_NUM / (double)P_DEN) * pow((double)E / K, 0.4))));

    W.init(); W.deduce();
    if(W.connected()) claimNow();

    Actor self;
    vector<vector<uint8_t>> bitsOf(K);
    vector<char> have(K,0);
    View V0, replayView;
    int epoch = 0;

    while(true){
        int P = baseP;
#if ADAPT_P == 1
        if(epoch >= 6) P = max(8, baseP / 2);
        else if(epoch >= 4) P = max(8, baseP * 2 / 3);
#elif ADAPT_P == 2
        int al = activeWaypointLinks(W);
        if(al <= 1) P = max(8, baseP / 2);
        else if(al <= max(2, (int)g_wp.size() / 4)) P = max(8, baseP * 2 / 3);
#elif ADAPT_P == 3
        int al = activeWaypointLinks(W);
        if(epoch >= 6 || al <= 1) P = max(8, baseP / 2);
        else if(epoch >= 4 || al <= max(2, (int)g_wp.size() / 4)) P = max(8, baseP * 2 / 3);
#elif ADAPT_P == 4
        if(K >= 10 && K < 16){
            int al = activeWaypointLinks(W);
            if(al <= 1) P = max(8, baseP / 2);
            else if(al <= max(2, (int)g_wp.size() / 4)) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 5
        if(K == 6 || (K >= 10 && K < 16)){
            int al = activeWaypointLinks(W);
            if(al <= 1) P = max(8, baseP / 2);
            else if(al <= max(2, (int)g_wp.size() / 4)) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 6
        if(K == 6 || (K >= 10 && K < 16)){
            int al = activeWaypointLinks(W);
            if(al <= 1) P = max(8, baseP / 2);
            else if(al <= 3) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 7
        if(K == 6 || (K >= 10 && K < 16)){
            int al = activeWaypointLinks(W);
            if(al <= 1) P = max(8, baseP / 2);
            else if(al <= 4) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 8
        if(K == 6 || (K >= 10 && K < 16)){
            int al = activeWaypointLinks(W);
            if(al <= 2) P = max(8, baseP / 2);
            else if(al <= 3) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 9
        if(K == 6 || K == 10 || K == 12){
            int al = activeWaypointLinks(W);
            if((K == 6 || K == 12) && al <= 2) P = max(8, baseP / 2);
            else if(al <= 1) P = max(8, baseP / 2);
            else if(al <= 3) P = max(8, baseP * 2 / 3);
        }
#elif ADAPT_P == 10
        if(K == 6 || K == 10 || K == 12){
            int al = activeWaypointLinks(W);
            if((K == 6 || K == 12) && al <= 3) P = max(8, baseP / 2);
            else if(al <= 1) P = max(8, baseP / 2);
            else if(al <= 3) P = max(8, baseP * 2 / 3);
        }
#endif
        epoch++;
        V0 = W;
        self.init(&W, ID);
        for(int o=0; o<K; o++){ bitsOf[o].clear(); have[o]=0; }

        for(int t=0; t<P; t++){
            int e = self.nextProbe();
            if(e<0) claimNow();
            int u = eA[e], r = 2*(u/m) + 1, c = 2*(u%m) + 1;
            if (eB[e] == u + 1) c++; else r++;
            say("? " + to_string(r) + " " + to_string(c));
            string reply = hear();
            bool open = (!reply.empty() && reply[0]=='1');
            bitsOf[ID].push_back(open?1:0);
            feedW(e, open);
        }
        have[ID]=1;
        if(K==1) continue;

        for(int j=0; j<d; j++){
            int span = 1<<j;
            {
                int partner = (ID + span) % K;
                vector<uint8_t> all;
                for(int t2=0; t2<span; t2++){
                    int o = ((ID - span + 1 + t2) % K + K) % K;
                    if((int)bitsOf[o].size()<P) bitsOf[o].resize(P,0);
                    for(int b=0; b<P; b++) all.push_back(bitsOf[o][b]);
                }
                say("> " + to_string(partner) + " " + encodeBits(all));
                hear();
            }
            {
                int sender = ((ID - span) % K + K) % K;
                say("< " + to_string(sender));
                string r = hear();
                size_t sp = r.find(' ');
                string body = (sp==string::npos) ? string() : r.substr(sp+1);
                if(r.size()>=1 && r[0]!='-'){
                    vector<uint8_t> all = decodeBits(body, span*P);
                    bool touchedW = false;
                    for(int t2=0; t2<span; t2++){
                        int o = ((sender - span + 1 + t2) % K + K) % K;
                        if(have[o]) continue;
                        bitsOf[o].assign(all.begin()+t2*P, all.begin()+(t2+1)*P);
                        have[o]=1;
                        replayView = V0;
                        Actor rep;
                        rep.init(&replayView, o);
                        for(int b=0; b<P; b++){
                            int e = rep.nextProbe();
                            if(e<0) break;
                            W.feedRaw(e, bitsOf[o][b]);
                            touchedW = true;
                            rep.feed(e, bitsOf[o][b]);
                        }
                    }
                    if(touchedW) finishWBatch();
                }
            }
        }
    }
}
