#include <bits/stdc++.h>
#pragma GCC optimize "O3"
#pragma GCC target "avx"
using namespace std;
struct disjoint_sets {
  vector<int> data;
  disjoint_sets() = default;
  explicit disjoint_sets(size_t n) : data(n, -1) {}
  bool is_root(int i) { return data[i] < 0; }
  int find_root(int i) {
    return is_root(i) ? i : (data[i] = find_root(data[i]));
  }
  int set_size(int i) { return -data[find_root(i)]; }
  int union_sets(int i, int j) {
    i = find_root(i);
    j = find_root(j);
    if (i != j) {
      if (set_size(i) < set_size(j)) swap(i, j);
      data[i] += data[j];
      data[j] = i;
    }
    return i;
  }
  bool is_same(int i, int j) { return find_root(i) == find_root(j); }
};
struct wrapped_disjoint_sets {
  vector<int> a_data;
  stack<pair<int, int> > modified;
  explicit wrapped_disjoint_sets(disjoint_sets const& a_original)
      : a_data(a_original.data) {}
  int data(int i) { return a_data[i]; }
  void data_set(int i, int j) {
    if (data(i) != j) {
      modified.emplace(i, a_data[i]);
      a_data[i] = j;
    }
  }
  void reset() {
    while (not modified.empty()) {
      int i, value;
      tie(i, value) = modified.top();
      modified.pop();
      a_data[i] = value;
    }
  }
  bool is_root(int i) { return data(i) < 0; }
  int find_root(int i) { return is_root(i) ? i : find_root(data(i)); }
  int set_size(int i) { return -data(find_root(i)); }
  int union_sets(int i, int j) {
    i = find_root(i);
    j = find_root(j);
    if (i != j) {
      if (set_size(i) < set_size(j)) swap(i, j);
      data_set(i, data(i) + data(j));
      data_set(j, i);
    }
    return i;
  }
  bool is_same(int i, int j) { return find_root(i) == find_root(j); }
};
int main() {
  int n, k, m;
  scanf("%d%d%d", &n, &k, &m);
  disjoint_sets splitted_ds(n);
  constexpr int bucket_width = 256;
  int bucket_count = ceil(n / (double)bucket_width);
  vector<vector<int> > edges(n);
  for (int i = 0; (i) < int(m); ++(i)) {
    int u, v;
    scanf("%d%d", &u, &v);
    --u;
    --v;
    if (u > v) swap(u, v);
    edges[u].push_back(v);
    int uj = u / bucket_width;
    int vj = v / bucket_width;
    if (uj == vj) {
      splitted_ds.union_sets(u, v);
    }
  }
  vector<int> cnts(bucket_count);
  for (int i = 0; (i) < int(n); ++(i)) {
    splitted_ds.find_root(i);
    if (splitted_ds.is_root(i)) {
      int j = i / bucket_width;
      ++cnts[j];
    }
  }
  wrapped_disjoint_sets wrapped_ds(splitted_ds);
  int q;
  scanf("%d", &q);
  while (q--) {
    int l, r;
    scanf("%d%d", &l, &r);
    --l;
    int lj = l / bucket_width;
    int rj = r / bucket_width;
    wrapped_ds.reset();
    int cnt = 0;
    {
      int i = l;
      for (; i < r and i / bucket_width == lj; ++i) {
        wrapped_ds.data_set(i, -1);
        ++cnt;
      }
      for (; i / bucket_width < rj; i += bucket_width) {
        cnt += cnts[i / bucket_width];
      }
      for (; i < r; ++i) {
        wrapped_ds.data_set(i, -1);
        ++cnt;
      }
    }
    {
      int i = l;
      for (; i < r and i / bucket_width == lj; ++i) {
        for (int j : edges[i])
          if (j < r) {
            if (not wrapped_ds.is_same(i, j)) {
              wrapped_ds.union_sets(i, j);
              --cnt;
            }
          }
      }
      for (; i / bucket_width < rj; i += bucket_width) {
        for (int di = bucket_width - k; di < bucket_width; ++di) {
          for (int j : edges[i + di])
            if (j < r) {
              if (not wrapped_ds.is_same(i + di, j)) {
                wrapped_ds.union_sets(i + di, j);
                --cnt;
              }
            }
        }
      }
      for (; i < r; ++i) {
        for (int j : edges[i])
          if (j < r) {
            if (not wrapped_ds.is_same(i, j)) {
              wrapped_ds.union_sets(i, j);
              --cnt;
            }
          }
      }
    }
    printf("%d\n", cnt);
  }
  return 0;
}
