#include <bits/stdc++.h>
inline long long rd() {
  long long _x = 0;
  int _ch = getchar(), _f = 1;
  for (; !isdigit(_ch) && (_ch != '-') && (_ch != EOF); _ch = getchar())
    ;
  if (_ch == '-') {
    _f = 0;
    _ch = getchar();
  }
  for (; isdigit(_ch); _ch = getchar()) _x = _x * 10 + _ch - '0';
  return _f ? _x : -_x;
}
void write(long long _x) {
  if (_x >= 10)
    write(_x / 10), putchar(_x % 10 + '0');
  else
    putchar(_x + '0');
}
inline void wrt(long long _x, char _p) {
  if (_x < 0) putchar('-'), _x = -_x;
  write(_x);
  if (_p) putchar(_p);
}
int fr[500005], to[500005], nxt[500005];
struct edge {
  int pos, c;
} upd[500005];
struct Graph {
  int f[500005], sz[500005];
  bool dis[500005];
  std::vector<std::pair<int, int> > vt;
  inline int find(int x) {
    for (; x != f[x]; x = f[x])
      ;
    return x;
  }
  inline bool querydis(int x) {
    bool ans = 0;
    for (; x != f[x]; x = f[x]) ans ^= dis[x];
    return ans;
  }
  bool Union(int x, int y) {
    int A = find(x), B = find(y);
    if (A == B) return 0;
    if (sz[A] < sz[B]) std::swap(A, B);
    dis[B] = querydis(x) ^ querydis(y) ^ 1;
    sz[A] += sz[B];
    f[B] = A;
    vt.push_back(std::make_pair(A, B));
    return 1;
  }
  void undo(int tot) {
    while (tot--) {
      std::pair<int, int> now = vt.back();
      sz[now.first] -= sz[now.second], f[now.second] = now.second;
      dis[now.second] = 0;
      vt.pop_back();
    }
  }
} g[51];
int C[500005];
bool flag;
int n, m, q, k;
std::vector<edge> tr[500005 << 2];
void update(int pos, int l, int r, int ql, int qr, edge now) {
  if (r < ql || l > qr) return;
  if (ql <= l && r <= qr) {
    tr[pos].push_back(now);
    return;
  }
  int mid = (l + r) >> 1;
  update(pos << 1, l, mid, ql, qr, now);
  update(pos << 1 | 1, mid + 1, r, ql, qr, now);
}
void solve(int pos, int l, int r) {
  int D[51];
  memset(D, 0, sizeof D);
  for (auto i : tr[pos]) {
    D[i.c] += g[i.c].Union(fr[i.pos], to[i.pos]);
  }
  if (l == r) {
    edge now = upd[l];
    if (((g[now.c].querydis(fr[now.pos]) ^ g[now.c].querydis(to[now.pos]))) ||
        (g[now.c].find(fr[now.pos]) != g[now.c].find(to[now.pos]))) {
      if (l + 1 <= nxt[l]) update(1, 1, q, l + 1, nxt[l], now);
      C[now.pos] = now.c;
      printf("YES\n");
    } else {
      if (l + 1 <= nxt[l] && C[now.pos])
        update(1, 1, q, l + 1, nxt[l], {now.pos, C[now.pos]});
      printf("NO\n");
    }
    for (int i = 1; i <= k; i++) {
      g[i].undo(D[i]);
    }
    return;
  }
  int mid = (l + r) >> 1;
  solve(pos << 1, l, mid), solve(pos << 1 | 1, mid + 1, r);
  for (int i = 1; i <= k; i++) {
    g[i].undo(D[i]);
  }
}
int Now[500005];
int main() {
  n = rd(), m = rd(), k = rd(), q = rd();
  for (int i = 1; i <= m; i++) {
    fr[i] = rd(), to[i] = rd();
  }
  for (int i = 1; i <= k; i++) {
    for (int j = 1; j <= n; j++) {
      g[i].f[j] = j, g[i].sz[j] = 1;
    }
  }
  for (int i = 1; i <= q; i++) nxt[i] = q;
  for (int i = 1; i <= q; i++) {
    int x = rd(), y = rd();
    if (Now[x]) nxt[Now[x]] = i - 1;
    upd[i] = {x, y};
    Now[x] = i;
  }
  solve(1, 1, q);
}
