#include <bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
#define sz(a) a.size()
#define pii pair<int,int>
#define fi first
#define se second
#define all(a) a.begin(), a.end()
using namespace std;
void setIO(string s) {
freopen((s + ".in").c_str(), "r", stdin);
freopen((s + ".out").c_str(), "w", stdout);
}
const int MAXN=2001;
const int MAXQ=1e5+1;
struct Tree {
int y;
int x;
int r;
};
struct Edge {
int u;
int v;
double w;
};
struct Query {
int r;
int st;
int idx;
};
int square(int n) {
return n*n;
}
int connected [5][5]; //is corner i connected to corner j
Tree trees [MAXN];
string res [MAXQ];
int sizes [MAXN];
int rep [MAXN];
int border [MAXN];
void build(int n) {
for(int i=1;i<=n;i++) {
rep[i]=i;
sizes[i]=1;
border[i]=0; //start with no borders, border goes UP RIGHT DOWN LEFT
}
}
int find(int v) {
if(v==rep[v])
return v;
rep[v]=find(rep[v]);
return rep[v];
}
bool unite(int u, int v) {
int pu=find(u);
int pv=find(v);
if(pu==pv)
return false;
if(sizes[pu]<sizes[pv])
swap(pu,pv);
sizes[pu]+=sizes[pv];
border[pu]|=border[pv];
rep[pv]=pu;
sizes[pv]=0; //make sure we don't check this again
//check for connectivity now
int mask [4] {0};
for(int i=0;i<4;i++)
mask[i]=(border[pu]>>i)&1;
if(mask[0]&&mask[2]) {
connected[1][2]=0;
connected[3][4]=0;
}
if(mask[1]&&mask[3]) {
connected[1][4]=0;
connected[2][3]=0;
}
if(mask[0]&&mask[1]) {
for(int i=1;i<=4;i++)
connected[min(i,3ll)][max(i,3ll)]=0;
}
if(mask[1]&&mask[2]) {
for(int i=1;i<=4;i++)
connected[min(i,2ll)][max(i,2ll)]=0;
}
if(mask[2]&&mask[3]) {
for(int i=1;i<=4;i++)
connected[min(i,1ll)][max(i,1ll)]=0;
}
if(mask[3]&&mask[0]) {
for(int i=1;i<=4;i++)
connected[min(i,4ll)][max(i,4ll)]=0;
}
return true;
}
void solve() {
//n<=2000 trees
//each pair of trees, distance can be fetched
//offline query DSU, as the person's size gets bigger, more trees link together to form a wall
//for each exit, if a component blocks that exist, we can't visit it
//for each component, check how many walls it touches, this can be represented as an integer <=15
//if any of the trees in a component is close enough to any of the 4 walls, add it to the list
//case work for can visit:
//1 cannot visit 2 if the barrier touches bottom wall, along with any other wall
int n,q;
cin>>n>>q;
int w,h;
cin>>w>>h;
for(int i=1;i<=4;i++) {
for(int j=1;j<=4;j++) {
connected[i][j]=1;
}
}
for(int i=1;i<=n;i++) {
//add all the trees
int x,y,r;
cin>>x>>y>>r;
trees[i]={y,x,r};
}
vector<Edge> edges;
for(int i=1;i<=n;i++) {
for(int j=i+1;j<=n;j++) {
edges.pb({i,j,sqrt((double)(square(trees[i].x-trees[j].x)+square(trees[i].y-trees[j].y)))-trees[i].r-trees[j].r});
}
}
sort(all(edges),[](const Edge&a, const Edge&b){return a.w<b.w;});
vector<Query> queries;
for(int i=1;i<=q;i++) {
//add all the visitors, offline query sort
int r,st;
cin>>r>>st;
queries.pb({r,st,i});
}
sort(all(queries), [](const Query&a, const Query&b){return a.r<b.r;});
build(n+1);
int ptr=0;
for(auto [r, st, idx]:queries) {
string ans;
//close all the gaps that we can't fit through, update border
for(int i=1;i<=n;i++) {
if(trees[i].x-2*r<0) {
border[find(i)]|=8;
}
if(trees[i].y-2*r<0) {
border[find(i)]|=4;
}
if(trees[i].x+2*r>w) {
border[find(i)]|=2;
}
if(trees[i].y+2*r>h) {
border[find(i)]|=1;
}
}
while(ptr<sz(edges)&&edges[ptr].w<(double)2.0*r) {
unite(edges[ptr].u, edges[ptr].v);
ptr++;
}
//after all edges united, check for connectivity
for(int i=1;i<=4;i++) {
if(i==st||connected[min(i,st)][max(i,st)])
ans+=to_string(i);
}
res[idx]=ans;
}
for(int i=1;i<=q;i++)
cout<<res[i]<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t=1;
// cin>>t;
while(t>0) {
solve();
t--;
}
}