#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
#include <numeric>
#include <cstdlib>
using namespace std;
int N,A,ID,MAXL;
string ask(const string&s){ cout<<s<<'\n'<<flush; string r; if(!getline(cin,r)) exit(0); return r; }
void halt(){ cout<<"halt\n"<<flush; exit(0); }
void claim(const string&p){ cout<<"! "<<p<<'\n'<<flush; exit(0); }
struct Edge{int a,b,r,c; int key1,key2,key3;};
struct DSU{ vector<int> p,sz; DSU(int n=0){p.resize(n); iota(p.begin(),p.end(),0); sz.assign(n,1);} int find(int x){while(p[x]!=x){p[x]=p[p[x]];x=p[x];}return x;} void unite(int a,int b){a=find(a);b=find(b); if(a==b)return; if(sz[a]<sz[b]) swap(a,b); p[b]=a; sz[a]+=sz[b];}};
string build_path(int m, vector<vector<pair<int,char>>> &adj){
 int goal=m*m-1; vector<int> par(m*m,-2); vector<char> pm(m*m); queue<int>q; par[0]=-1;q.push(0); while(!q.empty()&&par[goal]==-2){int v=q.front();q.pop(); for(auto [to,ch]:adj[v]) if(par[to]==-2){par[to]=v;pm[to]=ch;q.push(to); if(to==goal)break;}}
 if(par[goal]==-2) return string(); string rooms; for(int cur=goal;cur!=0;cur=par[cur]) rooms.push_back(pm[cur]); reverse(rooms.begin(),rooms.end()); string path; for(char ch:rooms){path.push_back(ch);path.push_back(ch);} return path;
}
vector<Edge> make_edges(int mode){
 int m=(N+1)/2; vector<Edge> e; e.reserve(2*m*(m-1));
 for(int i=0;i<m;i++) for(int j=0;j<m;j++){ int node=i*m+j; auto add=[&](int ni,int nj,int rr,int cc){ Edge ed; ed.a=node; ed.b=ni*m+nj; ed.r=rr; ed.c=cc; int x=i+ni, y=j+nj; // doubled room midpoint
 int diag=abs(x-y); int anti=abs((x+y)-2*(m-1)); int border=min(min(i,j), min(m-1-i,m-1-j));
 if(mode==0){ ed.key1=0; ed.key2=(int)e.size(); ed.key3=0; }
 else if(mode==1){ ed.key1=diag; ed.key2=x+y; ed.key3=(int)e.size(); }
 else if(mode==5){ ed.key1=diag; ed.key2=abs((x+y)-2*(m-1)); ed.key3=(int)e.size(); }
 else if(mode==6){ ed.key1=diag; ed.key2=-(x+y); ed.key3=(int)e.size(); }
 else if(mode==7){ ed.key1=diag; unsigned h=(unsigned)(ed.a*1103515245u + ed.b*12345u); ed.key2=(int)(h&0x7fffffff); ed.key3=(int)e.size(); }
 else if(mode==2){ ed.key1=anti; ed.key2=diag; ed.key3=(int)e.size(); }
 else if(mode==3){ ed.key1=min(diag, anti); ed.key2=max(diag,anti); ed.key3=(int)e.size(); }
 else { ed.key1=border; ed.key2=diag; ed.key3=(int)e.size(); }
 e.push_back(ed); };
 if(i+1<m) add(i+1,j,2*i+2,2*j+1); if(j+1<m) add(i,j+1,2*i+1,2*j+2); }
 if(mode!=0) stable_sort(e.begin(), e.end(), [](const Edge&a,const Edge&b){ if(a.key1!=b.key1)return a.key1<b.key1; if(a.key2!=b.key2)return a.key2<b.key2; return a.key3<b.key3; });
 return e;
}
void stream_scan(int mode){
 if(A<4){ /* use old full scan? central query all */ }
 int P=A-1; if(ID==0){
   int m=(N+1)/2; auto edges=make_edges(mode); int E=edges.size(); const int CH=56; auto cnt_for=[&](int rank){ if(rank>=E) return 0; return (E-1-rank)/P+1;}; int expected=0; for(int rank=0;rank<P;rank++){int cnt=cnt_for(rank); expected += (cnt+CH-1)/CH;}
   vector<int> chunkNo(P,0); DSU d(m*m); vector<vector<pair<int,char>>> adj(m*m); int got=0; auto addopen=[&](int pos){ if(pos<0||pos>=E)return; auto &ed=edges[pos]; int a=ed.a,b=ed.b; int ar=a/m,ac=a%m,br=b/m,bc=b%m; char ca,cb; if(br==ar+1){ca='D';cb='U';} else if(br==ar-1){ca='U';cb='D';} else if(bc==ac+1){ca='R';cb='L';} else {ca='L';cb='R';} adj[a].push_back({b,ca}); adj[b].push_back({a,cb}); d.unite(a,b); };
   while(got<expected){ string rep=ask("< ?"); if(rep.size()>=3 && rep[0]!='-'){ size_t sp=rep.find(' '); int sender=stoi(rep.substr(0,sp)); string body=rep.substr(sp+1); int rank=sender-1; if(rank>=0&&rank<P){ int startBit=chunkNo[rank]*CH; int bit=0; for(char ch:body){ int val=(unsigned char)ch-35; for(int b=0;b<6 && bit<CH;b++,bit++){ if(val>>b&1){ int kth=startBit+bit; int pos=rank + kth*P; addopen(pos); } } } chunkNo[rank]++; got++; if(d.find(0)==d.find(m*m-1)){ string p=build_path(m,adj); if(!p.empty()) claim(p); } } } }
   string p=build_path(m,adj); if(!p.empty()) claim(p); halt();
 } else {
   int rank=ID-1; auto edges=make_edges(mode); int E=edges.size(); const int CH=56; vector<int> bits; bits.reserve(CH); auto sendbuf=[&](){ if(bits.empty()) return; string s; for(int k=0;k<(int)bits.size();k+=6){int val=0; for(int b=0;b<6 && k+b<(int)bits.size();b++) if(bits[k+b]) val|=1<<b; s.push_back(char(35+val));} ask("> 0 "+s); bits.clear(); };
   for(int pos=rank; pos<E; pos+=P){ string rep=ask("? "+to_string(edges[pos].r)+" "+to_string(edges[pos].c)); bits.push_back(!rep.empty()&&rep[0]=='1'); if((int)bits.size()==CH) sendbuf(); }
   sendbuf(); halt();
 }
}
// fallback full_scan from previous? use stream mode 0 maybe full order with central receiver; for A=3 maybe old better but okay

void old_full_scan(int P){
    if(ID>=P) halt();
    int m=(N+1)/2;
    struct Edge{int a,b,r,c;};
    vector<Edge> edges;
    edges.reserve(2*m*(m-1));
    for(int i=0;i<m;i++) for(int j=0;j<m;j++){
        int node=i*m+j;
        if(i+1<m) edges.push_back({node,(i+1)*m+j,2*i+2,2*j+1});
        if(j+1<m) edges.push_back({node,i*m+j+1,2*i+1,2*j+2});
    }
    int E=(int)edges.size();
    vector<int> mybits;
    for(int idx=ID; idx<E; idx+=P){
        string rep=ask("? "+to_string(edges[idx].r)+" "+to_string(edges[idx].c));
        mybits.push_back(!rep.empty() && rep[0]=='1');
    }
    const int BPC=256*6;
    auto encode=[&](const vector<int>& bits)->vector<string>{
        vector<string> out;
        for(int pos=0; pos<(int)bits.size(); pos+=BPC){
            int len=min(BPC,(int)bits.size()-pos);
            string s;
            for(int k=0;k<len;k+=6){
                int val=0;
                for(int b=0;b<6 && k+b<len;b++) if(bits[pos+k+b]) val |= (1<<b);
                s.push_back(char(35+val));
            }
            out.push_back(s);
        }
        return out;
    };
    if(ID!=0){
        vector<string> chunks=encode(mybits);
        for(string &s: chunks) ask("> 0 "+s);
        halt();
    }
    vector<int> open(E,0);
    int p=0;
    for(int idx=0; idx<E; idx+=P) open[idx]=mybits[p++];
    auto count_for=[&](int id){ if(id>=E) return 0; return (E-1-id)/P + 1; };
    int expected=0;
    for(int id=1; id<P; ++id){ int cnt=count_for(id); expected += (cnt + BPC-1)/BPC; }
    vector<string> recvdata(P);
    int got=0;
    while(got<expected){
        string rep=ask("< ?");
        if(rep.size()>=3 && rep[0]!='-'){
            size_t sp=rep.find(' ');
            int sender=stoi(rep.substr(0,sp));
            string body = (sp==string::npos? string() : rep.substr(sp+1));
            if(sender>=0 && sender<P){ recvdata[sender] += body; got++; }
        }
    }
    for(int id=1; id<P; ++id){
        int cnt=count_for(id); int bitpos=0;
        for(char ch: recvdata[id]){
            int val=int((unsigned char)ch)-35;
            for(int b=0;b<6 && bitpos<cnt;b++,bitpos++){
                int idx=id + bitpos*P;
                if(idx<E) open[idx]=(val>>b)&1;
            }
        }
    }
    vector<vector<pair<int,char>>> adj(m*m);
    for(int i=0;i<E;i++) if(open[i]){
        int a=edges[i].a,b=edges[i].b;
        int ar=a/m, ac=a%m, br=b/m, bc=b%m;
        char ca='?', cb='?';
        if(br==ar+1){ ca='D'; cb='U'; }
        else if(br==ar-1){ ca='U'; cb='D'; }
        else if(bc==ac+1){ ca='R'; cb='L'; }
        else { ca='L'; cb='R'; }
        adj[a].push_back({b,ca}); adj[b].push_back({a,cb});
    }
    int goal=m*m-1;
    vector<int> par(m*m,-2); vector<char> pm(m*m,0); queue<int> q;
    par[0]=-1; q.push(0);
    while(!q.empty() && par[goal]==-2){
        int v=q.front(); q.pop();
        for(auto [to,ch]: adj[v]) if(par[to]==-2){ par[to]=v; pm[to]=ch; q.push(to); if(to==goal) break; }
    }
    if(par[goal]==-2){ halt(); }
    string rooms;
    for(int cur=goal; cur!=0; cur=par[cur]) rooms.push_back(pm[cur]);
    reverse(rooms.begin(), rooms.end());
    string path;
    for(char ch: rooms){ path.push_back(ch); path.push_back(ch); }
    claim(path);
}



void solo_dfs(){
    int m=(N+1)/2; int total=m*m; int goal=total-1;
    vector<array<int,4>> orders;
    orders.push_back({1,3,0,2}); // D,R,U,L
    orders.push_back({3,1,2,0}); // R,D,L,U
    orders.push_back({1,3,2,0});
    orders.push_back({3,1,0,2});
    orders.push_back({1,0,3,2});
    orders.push_back({3,2,1,0});
    int variant=ID%(int)orders.size(); auto ord=orders[variant];
    int dr[4]={-1,1,0,0}, dc[4]={0,0,-1,1}; char chs[4]={'U','D','L','R'}; char inv[256]; inv['U']='D'; inv['D']='U'; inv['L']='R'; inv['R']='L';
    bool fromEnd = (ID%2==1 && A>1);
    int start = fromEnd? goal:0; int target=fromEnd?0:goal;
    vector<char> seen(total,0); vector<int> st; vector<int> nxt(total,0); vector<char> path;
    st.push_back(start); seen[start]=1;
    while(!st.empty()){
        int v=st.back(); if(v==target){ string out; if(!fromEnd){ for(char c:path){out.push_back(c);out.push_back(c);} } else { for(int i=(int)path.size()-1;i>=0;i--){ char c=inv[(int)path[i]]; out.push_back(c); out.push_back(c);} } claim(out); }
        if(nxt[v]>=4){ st.pop_back(); if(!path.empty()) path.pop_back(); continue; }
        int dir=ord[nxt[v]++]; int r=v/m, c=v%m; int nr=r+dr[dir], nc=c+dc[dir]; if(nr<0||nr>=m||nc<0||nc>=m) continue; int to=nr*m+nc; if(seen[to]) continue;
        int qr=r+nr+1; int qc=c+nc+1; // grid corridor: room coords 0-based -> odd 2r+1, midpoint = r+nr+1
        string rep=ask("? "+to_string(qr)+" "+to_string(qc));
        if(!rep.empty()&&rep[0]=='1'){ seen[to]=1; st.push_back(to); path.push_back(chs[dir]); }
    }
    halt();
}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr); if(!(cin>>N>>A>>ID>>MAXL)) return 0; string dummy; getline(cin,dummy); if(A<4) solo_dfs(); else stream_scan(1); return 0;}
