# include <algorithm>
# include <cassert>
# include <cstdio>
# include <cstring>
# include <map>
# include <set>
# include <vector>
using namespace std;
# define x first
# define y second
typedef pair <int, int> PR;
const char *FIN = "obiective.in", *FOU = "obiective.out";
const int MAX_N = 32005, MAX_M = 64005, MAX_NIV = 19;
PR obj[MAX_M];
bool viz[MAX_N];
int N, M, T, RAD, nrcomp, sq_len, P[MAX_N], grad[MAX_N], st[MAX_N], dr[MAX_N], niv[MAX_N], node[MAX_NIV][MAX_N];
vector <int> sol, G[MAX_N], Gn[MAX_N], Gt[MAX_N];
map <int, int> comp;
set <PR> SET;
void dfp (int P) { // marchez cu + graful G
viz[P] = 1;
for (vector <int> :: iterator it = Gn[P].begin (); it != Gn[P].end (); ++it)
if (viz[*it] == 0)
dfp (*it);
sol.push_back (P);
}
void dfm (int P, int cat) { // marchez cu - graful transpus lui G
viz[P] = 1, comp[P] = cat;
for (vector <int> :: iterator it = Gt[P].begin (); it != Gt[P].end (); ++it)
if (viz[*it] == 0)
dfm (*it, cat);
}
void tareconexe (void) {
for (int i = 1; i <= N; ++i)
if (viz[i] == 0) dfp (i);
memset (viz, 0, sizeof (viz));
for (nrcomp = 0; sol.size (); sol.pop_back ()) // parcurg invers elementele care le-am parcurs in DF+
if (viz[sol.back ()] == 0)
dfm (sol.back (), ++nrcomp);
for (int i = 1; i <= M; ++i) {
obj[i] = make_pair (comp[obj[i].x], comp[obj[i].y]);
if (obj[i].x != obj[i].y && SET.count (obj[i]) == 0) {
G[obj[i].x].push_back (obj[i].y), grad[obj[i].y] += 1;
SET.insert (obj[i]);
}
}
}
inline bool compare (const int &a, const int &b) {
return P[a] < P[b];
}
void dfsT (int S) {
viz[S] = 1;
for (vector <int> :: iterator it = G[S].begin (); it != G[S].end (); ++it)
if (viz[*it] == 0)
dfsT (*it);
sol.push_back (S);
}
void topologicals (void) {
sol.clear (), memset (viz, 0, sizeof (viz));
for (int i = 1; i <= nrcomp; ++i)
if (viz[i] == 0) dfsT (i);
reverse (sol.begin (), sol.end ());
for (size_t i = 0; i < sol.size (); ++i)
P[sol[i]] = i;
for (int i = 1; i <= nrcomp; ++i)
sort (G[i].begin (), G[i].end (), compare);
}
void dfsL (int S) {
viz[S] = 1, st[S] = ++sq_len;
for (vector <int> :: iterator it = G[S].begin (); it != G[S].end (); ++it)
if (viz[*it] == 0)
dfsL (*it);
dr[S] = sq_len;
}
void liniarize (void) {
memset (viz, 0, sizeof (viz)), dr[0] = 0x3f3f3f3f;
for (int i = 1; i <= nrcomp; ++i)
if (grad[i] == 0) {
RAD = i, dfsL (i);
break;
}
}
void dfs1 (int S) {
viz[S] = 1;
for (vector <int> :: iterator it = G[S].begin (); it != G[S].end (); ++it)
if (viz[*it]) {
if (niv[*it] > niv[S]) node[0][*it] = S;
niv[*it] = min (niv[*it], niv[S]);
} else {
niv[*it] = niv[S] + 1, node[0][*it] = S;
dfs1 (*it);
if (niv[*it] < niv[S])
niv[S] = niv[*it], node[0][S] = node[0][*it];
}
}
void dfs2 (int S) {
viz[S] = 1;
for (vector <int> :: iterator it = G[S].begin (); it != G[S].end (); ++it) {
if (viz[*it] == 0) dfs2 (*it);
if (niv[*it] < niv[S])
niv[S] = niv[*it], node[0][S] = node[0][*it];
}
}
void buildascensors () {
memset (viz, 0, sizeof (viz)), dfs1 (RAD);
memset (viz, 0, sizeof (viz)), dfs2 (RAD);
for (int i = 1; i < MAX_NIV; ++i)
for (int j = 1; j <= nrcomp; ++j)
node[i][j] = node[i - 1][node[i - 1][j]];
}
inline bool ascensor (int a, int b) {
return st[b] <= st[a] && dr[a] <= dr[b];
}
int main (void) {
assert (freopen (FIN, "r", stdin));
assert (freopen (FOU, "w", stdout));
assert (scanf ("%d %d", &N, &M) == 2);
for (int i = 1; i <= M; ++i) {
assert (scanf ("%d %d", &obj[i].x, &obj[i].y) == 2);
Gn[obj[i].x].push_back (obj[i].y);
Gt[obj[i].y].push_back (obj[i].x);
}
tareconexe (), topologicals (), liniarize (), buildascensors ();
for (assert (scanf ("%d", &T) == 1); T; --T) {
int x, y;
scanf ("%d %d", &x, &y), x = comp[x], y = comp[y];
if (x == y || ascensor (y, x))
printf ("0\n");
else {
int solution = 0, nod = x;
for (int j = MAX_NIV - 1; j + 1; --j)
if (!ascensor (y, node[j][nod]))
nod = node[j][nod], solution += 1 << j;
if (ascensor (y, node[0][nod])) solution += 1;
printf ("%d\n", solution);
}
}
}