// MCC 2026 Problem D — Datacenter Imprisonment.
//
// One binary, two cooperative strategies, chosen at startup from (N, NUM_AGENTS):
//
//   DIRECTED (work-stealing bidirectional search) — dependency-bound, ~flat in A.
//     Wins when agents are scarce relative to the maze. agent 0 seeds two tasks
//     (explore from S toward T and from T toward S) and hands tasks to idle
//     walkers; each walker runs a budgeted greedy DFS over its subtree, streams
//     the open tree-edges it finds, and re-queues its leftover frontier on a cut.
//     agent 0 unions every edge and claims the instant S and T connect.
//
//   SCAN (parallel passage scan) — throughput-bound, ~P/(A-1) turns.
//     Wins when agents are plentiful. Probers own disjoint passage stripes and
//     ship the open ones to agent 0, which unions and claims on connect.
//
// Every agent makes the same deterministic choice, so all agree on the protocol.
// Score = max turns any agent took at the claim (fewer is better).
#include <cstdio>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
using namespace std;

static int N, A, ID, MSG, m;
static long long DN, P;

static string readline(){
    string s; int ch;
    while((ch=getchar())!=EOF && ch!='\n') s.push_back((char)ch);
    if(ch==EOF && s.empty()) exit(0);
    return s;
}
static inline int rid(int i,int j){return i*m+j;}
static const int di[4]={-1,1,0,0}, dj[4]={0,0,-1,1};   // U D L R ; opposite = d^1

// Global passage index for the edge leaving room (i,j) in direction d.
static inline long long pidx(int i,int j,int d){
    switch(d){
        case 1: return (long long)i*m+j;              // Down
        case 0: return (long long)(i-1)*m+j;          // Up
        case 3: return DN + (long long)i*(m-1)+j;     // Right
        default:return DN + (long long)i*(m-1)+(j-1); // Left
    }
}
static inline void pcell(int i,int j,int d,int&pr,int&pc){ pr=2*i+1+di[d]; pc=2*j+1+dj[d]; }
// Decode a passage index to its two rooms and the ULDR chars for each direction.
static inline void decode(long long idx,int&ra,int&rb,char&ca,char&cb,int&pr,int&pc){
    if(idx<DN){ int i=idx/m,j=idx%m; ra=rid(i,j); rb=rid(i+1,j); ca='D'; cb='U'; pr=2*i+2; pc=2*j+1; }
    else{ long long r=idx-DN; int i=r/(m-1),j=r%(m-1); ra=rid(i,j); rb=rid(i,j+1); ca='R'; cb='L'; pr=2*i+1; pc=2*j+2; }
}

// ---- shared aggregator state + claim -----------------------------------------
static vector<int> par; static vector<char> rnk; static vector<char> seen;
static vector<vector<pair<int,char>>> adj;
static int uf_find(int x){ while(par[x]!=x){ par[x]=par[par[x]]; x=par[x]; } return x; }
static void uf_unite(int a,int b){ a=uf_find(a); b=uf_find(b); if(a!=b){ if(rnk[a]<rnk[b])swap(a,b); par[b]=a; if(rnk[a]==rnk[b])rnk[a]++; } }
static void ingest(long long idx){ int ra,rb,pr,pc; char ca,cb; decode(idx,ra,rb,ca,cb,pr,pc);
    adj[ra].push_back({rb,ca}); adj[rb].push_back({ra,cb}); uf_unite(ra,rb); seen[ra]=1; seen[rb]=1; }
static void agg_init(){ par.resize(m*m); rnk.assign(m*m,0); for(int i=0;i<m*m;i++)par[i]=i; adj.assign(m*m,{}); seen.assign(m*m,0); }
// BFS S->T over confirmed edges, expand each room-step x2, claim.
static void claim_path(){
    const int S=rid(0,0), T=rid(m-1,m-1);
    vector<int> ppar(m*m,-2); vector<char> pdir(m*m,0);
    vector<int> q; q.push_back(S); ppar[S]=-1;
    for(size_t h=0;h<q.size();h++){ int u=q[h]; if(u==T)break;
        for(auto&e:adj[u]) if(ppar[e.first]==-2){ ppar[e.first]=u; pdir[e.first]=e.second; q.push_back(e.first);} }
    string moves,rev; { int cur=T; while(cur!=S && cur>=0){ rev.push_back(pdir[cur]); cur=ppar[cur]; }
        for(int i=(int)rev.size()-1;i>=0;i--){ moves.push_back(rev[i]); moves.push_back(rev[i]); } }
    printf("! %s\n",moves.c_str()); fflush(stdout);
}

// ============================== SCAN ==========================================
static void run_scan(){
    const int S=rid(0,0), T=rid(m-1,m-1);
    if(ID!=0 && A>1){                          // prober: own passages idx % (A-1) == ID-1
        int stripe=A-1, off=ID-1; string batch; int cnt=0; const int BATCH=30;
        auto flush=[&](){ if(!cnt)return; printf("> 0 %s\n",batch.c_str()); fflush(stdout); readline(); batch.clear(); cnt=0; };
        for(long long idx=off; idx<P; idx+=stripe){
            int ra,rb,pr,pc; char ca,cb; decode(idx,ra,rb,ca,cb,pr,pc);
            printf("? %d %d\n",pr,pc); fflush(stdout); string s=readline();
            if(!s.empty()&&s[0]=='1'){ if(cnt)batch.push_back(' '); batch+=to_string(idx); if(++cnt>=BATCH)flush(); }
        }
        flush(); printf("halt\n"); fflush(stdout); return;
    }
    agg_init();
    if(A==1){ for(long long idx=0;idx<P;idx++){ int ra,rb,pr,pc;char ca,cb; decode(idx,ra,rb,ca,cb,pr,pc);
                printf("? %d %d\n",pr,pc); fflush(stdout); string s=readline(); if(!s.empty()&&s[0]=='1') ingest(idx); } }
    else {
        while(uf_find(S)!=uf_find(T)){
            printf("< ?\n"); fflush(stdout); string s=readline();
            if(s.size()<2||s[0]=='-') continue;
            size_t sp=s.find(' '); if(sp==string::npos) continue;
            const char* p=s.c_str()+sp+1;
            while(*p){ while(*p==' ')p++; if(!*p)break; char*e; long long idx=strtoll(p,&e,10); if(e==p)break; ingest(idx); p=e; }
        }
    }
    claim_path();
}

// ============================== DIRECTED ======================================
static vector<int> vis; static int gen=0;
struct Frame { int i,j; int ord[4]; int k; int forb; };
static void run_directed(){
    const int S=rid(0,0), T=rid(m-1,m-1);
    if(ID!=0 && A>1){                          // walker
        vis.assign(m*m,-1);
        // Persistent per-walker probe cache (passage state is immutable): a walker
        // never re-probes or re-reports a passage it has already seen across tasks.
        vector<unsigned char> pcache((size_t)P,0);   // 0 unknown,1 open,2 closed
        string ebuf; int ecnt=0;
        auto esend=[&](){ if(!ecnt)return; string body="E "+ebuf; printf("> 0 %s\n",body.c_str()); fflush(stdout); readline(); ebuf.clear(); ecnt=0; };
        auto orderDirs=[&](int i,int j,int gi,int gj,int ord[4]){
            int cost[4];
            for(int d=0;d<4;d++){ int ni=i+di[d],nj=j+dj[d];
                cost[d]=(ni<0||nj<0||ni>=m||nj>=m)?(1<<30):abs(ni-gi)+abs(nj-gj); ord[d]=d; }
            for(int a=0;a<4;a++)for(int b=a+1;b<4;b++) if(cost[ord[b]]<cost[ord[a]]){int t=ord[a];ord[a]=ord[b];ord[b]=t;}
        };
        printf("> 0 Q\n"); fflush(stdout); readline();
        const int BUDGET=256;
        while(true){
            string as;
            while(true){ printf("< 0\n"); fflush(stdout); as=readline(); if(!as.empty()&&as[0]!='-') break; }
            int sndr,rr,mm,gg; char tag;
            if(sscanf(as.c_str(),"%d %c %d %d %d",&sndr,&tag,&rr,&mm,&gg)!=5 || tag!='A') continue;
            int gi=gg?(m-1):0, gj=gg?(m-1):0;
            gen++;
            vector<Frame> st;
            { Frame fr; fr.i=rr/m; fr.j=rr%m; fr.forb=mm; fr.k=0; orderDirs(fr.i,fr.j,gi,gj,fr.ord); st.push_back(fr); vis[rr]=gen; }
            int budget=BUDGET;
            string tbuf; int tcnt=0;
            auto tflush=[&](){ if(!tcnt)return; string body="T "+tbuf; printf("> 0 %s\n",body.c_str()); fflush(stdout); readline(); tbuf.clear(); tcnt=0; };
            auto tpush=[&](int r,int mask,int g){ if(tcnt)tbuf.push_back(' '); tbuf+=to_string(r); tbuf.push_back(' '); tbuf+=to_string(mask); tbuf.push_back(' '); tbuf+=to_string(g); if(++tcnt>=16) tflush(); };
            while(!st.empty() && budget>0){
                int top=(int)st.size()-1; bool descended=false;
                while(st[top].k<4){
                    int ci=st[top].i, cj=st[top].j; int d=st[top].ord[st[top].k++];
                    if(st[top].forb&(1<<d)) continue;
                    int ni=ci+di[d], nj=cj+dj[d];
                    if(ni<0||nj<0||ni>=m||nj>=m) continue;
                    int nr=rid(ni,nj); if(vis[nr]==gen) continue;
                    long long idx=pidx(ci,cj,d);
                    unsigned char &cst=pcache[idx];
                    bool open;
                    if(cst){                                   // cache hit: no turn, already reported by us
                        open=(cst==1);
                    } else {                                   // cache miss: probe (costs a turn), report if open
                        int pr,pc; pcell(ci,cj,d,pr,pc); budget--;
                        printf("? %d %d\n",pr,pc); fflush(stdout); string s=readline();
                        open=(!s.empty()&&s[0]=='1'); cst=open?1:2;
                        if(open){ if(ecnt)ebuf.push_back(' '); ebuf+=to_string(idx); if(++ecnt>=28) esend(); }
                    }
                    if(open){
                        vis[nr]=gen;
                        Frame ch; ch.i=ni; ch.j=nj; ch.forb=(1<<(d^1)); ch.k=0; orderDirs(ni,nj,gi,gj,ch.ord);
                        st.push_back(ch); descended=true; break;
                    }
                    if(budget<=0) break;
                }
                if(descended) continue;
                if(st.back().k>=4) st.pop_back(); else break;
            }
            if(budget<=0){
                for(auto &fr: st){
                    bool has=false;
                    for(int t=fr.k;t<4;t++){ int d=fr.ord[t]; int ni=fr.i+di[d],nj=fr.j+dj[d];
                        if(ni<0||nj<0||ni>=m||nj>=m) continue; if(vis[rid(ni,nj)]==gen) continue; has=true; }
                    if(!has) continue;
                    int mask=fr.forb; for(int t=0;t<fr.k;t++) mask|=(1<<fr.ord[t]);
                    tpush(rid(fr.i,fr.j), mask, gg);
                }
            }
            esend(); tflush();
            printf("> 0 Q\n"); fflush(stdout); readline();
        }
    }

    // aggregator
    agg_init();
    if(A==1){ for(long long idx=0;idx<P;idx++){ int ra,rb,pr,pc;char ca,cb; decode(idx,ra,rb,ca,cb,pr,pc);
                printf("? %d %d\n",pr,pc); fflush(stdout); string s=readline(); if(!s.empty()&&s[0]=='1') ingest(idx); } claim_path(); return; }
    struct Task{int r,mask,g,pri;};
    auto cmp=[](const Task&a,const Task&b){return a.pri>b.pri;};
    priority_queue<Task,vector<Task>,decltype(cmp)> Q(cmp);
    auto prio=[&](int r,int g){int i=r/m,j=r%m; int gi=g?(m-1):0,gj=g?(m-1):0; return abs(i-gi)+abs(j-gj);};
    Q.push({S,0,1,prio(S,1)}); Q.push({T,0,0,prio(T,0)});
    vector<int> idle;

    // Enclosure prune ("dead pocket"): a task at room r toward goal-corner is dropped iff a
    // capped flood over UNCONFIRMED rooms out of r stays bounded AND never reaches the goal's
    // confirmed component. By the tree leave-once property, the path can't traverse a region
    // sealed by confirmed cells + the map boundary, so such a task can never help connect S-T.
    // Only prunes provably-dead bounded pockets (escape-the-cap => kept), so it can't break
    // correctness. `sides of the map` = the boundary half of the barrier.
    vector<int> fvis(m*m,0); int fgen=0; vector<int> fq; fq.reserve(m*m);
    const int KCAP = 12*m + 64;
    auto dead_pocket=[&](int r)->bool{
        if(!seen[r]) return false;                      // r not yet confirmed: keep (safe)
        int rc=uf_find(r);
        fgen++; fq.clear();
        int ri=r/m, rj=r%m;
        for(int d=0;d<4;d++){ int ni=ri+di[d],nj=rj+dj[d]; if(ni<0||nj<0||ni>=m||nj>=m)continue;
            int nr=rid(ni,nj); if(seen[nr]||fvis[nr]==fgen)continue; fvis[nr]=fgen; fq.push_back(nr); }
        int head=0;
        while(head<(int)fq.size()){
            if(head>KCAP) return false;                 // escaped cap: assume alive (safe)
            int u=fq[head++]; int ui=u/m, uj=u%m;
            for(int d=0;d<4;d++){ int vi=ui+di[d],vj=uj+dj[d]; if(vi<0||vj<0||vi>=m||vj>=m)continue;
                int v=rid(vi,vj);
                if(seen[v]){ if(uf_find(v)!=rc) return false; }     // could bridge another component: alive
                else if(fvis[v]!=fgen){ fvis[v]=fgen; fq.push_back(v); } }
        }
        return true;                                    // bounded, reconnects only to r's own comp: dead
    };

    while(uf_find(S)!=uf_find(T)){
        if(!idle.empty() && !Q.empty()){
            Task t=Q.top(); Q.pop();
            if(dead_pocket(t.r)) continue;                           // drop dead task, keep walker idle
            int w=idle.back(); idle.pop_back();
            printf("> %d A %d %d %d\n", w, t.r, t.mask, t.g); fflush(stdout); readline();
        } else {
            printf("< ?\n"); fflush(stdout); string s=readline();
            if(s.size()<3||s[0]=='-') continue;
            char* p=(char*)s.c_str(); int sndr=strtol(p,&p,10);
            while(*p==' ')p++; char tag=*p; if(!tag)continue; p++;
            if(tag=='E'){ while(*p){ while(*p==' ')p++; if(!*p)break; char*e; long long idx=strtoll(p,&e,10); if(e==p)break; ingest(idx); p=e; if(uf_find(S)==uf_find(T))break; } }
            else if(tag=='T'){ while(*p){ while(*p==' ')p++; if(!*p)break; char*e;
                    int r=strtol(p,&e,10); if(e==p)break; p=e; while(*p==' ')p++;
                    int mask=strtol(p,&e,10); p=e; while(*p==' ')p++;
                    int g=strtol(p,&e,10); p=e; Q.push({r,mask,g,prio(r,g)}); } }
            else if(tag=='Q'){ idle.push_back(sndr); }
        }
    }
    claim_path();
}

int main(){
    { string f=readline(); if(sscanf(f.c_str(),"%d %d %d %d",&N,&A,&ID,&MSG)!=4) return 0; }
    if(N==1){ if(ID==0){ printf("! \n"); fflush(stdout);} return 0; }
    m=(N+1)/2; DN=(long long)(m-1)*m; P=2*(long long)(m-1)*m;

    // Strategy pick (deterministic, identical across all agents).
    // scan costs ~P/(A-1) turns; directed's capability floor ~0.13*m^2 (empirical).
    double scan_est = (A>1) ? (double)P/(A-1) : 1e18;
    double dcap     = 330.0; if(0.13*(double)m*m > dcap) dcap = 0.13*(double)m*m;
    bool useScan = (A<=1) ? true : (scan_est <= dcap);

    if(useScan) run_scan(); else run_directed();
    return 0;
}
