#include <bits/stdc++.h>
using namespace std;
void Rd(int &x) {
  static char c;
  x = 0;
  while ((c = getchar()) < 48)
    ;
  do x = (x << 3) + (x << 1) + (c ^ 48);
  while ((c = getchar()) > 47);
}
static const int M = 100004;
int n, m, Fa[M];
int Next[M], V[M], Head[M], tot;
void Add_Edge(int u, int v) { Next[++tot] = Head[u], V[Head[u] = tot] = v; }
namespace P1 {
bool P[M];
void DFS(int x) {
  if (!P[x]) {
    P[x] = 1;
    return;
  }
  for (int i = Head[x]; i; i = Next[i]) DFS(V[i]);
}
void Del(int x) {
  P[x] = 0;
  for (int i = Head[x]; i; i = Next[i]) Del(V[i]);
}
void Solve() {
  while (m--) {
    int Op, x;
    Rd(Op), Rd(x);
    if (Op == 1)
      DFS(x);
    else if (Op == 2)
      Del(x);
    else
      puts(P[x] ? "Yes" : "No");
  }
}
};  // namespace P1
namespace P2 {
int Sz[M], Dep[M], Son[M], Top[M], ID[M], Ed[M], dfn;
void DFS(int x) {
  Sz[x] = 1;
  for (int i = Head[x]; i; i = Next[i]) {
    int y = V[i];
    Dep[y] = Dep[x] + 1;
    DFS(y);
    Sz[x] += Sz[y];
    if (Sz[y] > Sz[Son[x]]) Son[x] = y;
  }
}
void RDFS(int x, int tp) {
  Top[x] = tp;
  ID[x] = ++dfn;
  if (Son[x]) RDFS(Son[x], tp);
  for (int i = Head[x]; i; i = Next[i])
    if (V[i] != Son[x]) RDFS(V[i], V[i]);
  Ed[x] = dfn;
}
int G[M];
struct Node {
  int x, f;
  Node operator+(const Node &_) {
    if (f >= _.x) return (Node){x, f - _.x + _.f};
    return (Node){x + _.x - f, _.f};
  }
} Tr[M << 2];
bool Del[M << 2];
void Build(int l, int r, int p) {
  if (l == r) {
    Tr[p] = (Node){1, 0};
    return;
  }
  int mid = l + r >> 1;
  Build(l, mid, (p << 1));
  Build(mid + 1, r, (p << 1 | 1));
  Tr[p] = Tr[(p << 1)] + Tr[(p << 1 | 1)];
}
void Down(int l, int r, int p) {
  if (Del[p]) {
    int mid = l + r >> 1;
    Tr[(p << 1)] = (Node){mid - l + 1, 0};
    Tr[(p << 1 | 1)] = (Node){r - mid, 0};
    Del[(p << 1)] = Del[(p << 1 | 1)] = 1, Del[p] = 0;
  }
}
Node Query(int l, int r, int p, int a, int b) {
  if (a <= l && r <= b) return Tr[p];
  int mid = l + r >> 1;
  Down(l, r, p);
  if (b <= mid) return Query(l, mid, (p << 1), a, b);
  if (a > mid) return Query(mid + 1, r, (p << 1 | 1), a, b);
  return Query(l, mid, (p << 1), a, b) + Query(mid + 1, r, (p << 1 | 1), a, b);
}
void Updata(int l, int r, int p, int a) {
  if (l == r) {
    ++Tr[p].f;
    return;
  }
  int mid = l + r >> 1;
  Down(l, r, p);
  if (a <= mid)
    Updata(l, mid, (p << 1), a);
  else
    Updata(mid + 1, r, (p << 1 | 1), a);
  Tr[p] = Tr[(p << 1)] + Tr[(p << 1 | 1)];
}
void Updata(int l, int r, int p, int a, int t) {
  if (l == r) {
    Tr[p].x += t;
    return;
  }
  int mid = l + r >> 1;
  Down(l, r, p);
  if (a <= mid)
    Updata(l, mid, (p << 1), a, t);
  else
    Updata(mid + 1, r, (p << 1 | 1), a, t);
  Tr[p] = Tr[(p << 1)] + Tr[(p << 1 | 1)];
}
void Delete(int l, int r, int p, int a, int b) {
  if (a <= l && r <= b) {
    Tr[p] = (Node){r - l + 1, 0};
    Del[p] = 1;
    return;
  }
  int mid = l + r >> 1;
  Down(l, r, p);
  if (b <= mid)
    Delete(l, mid, (p << 1), a, b);
  else if (a > mid)
    Delete(mid + 1, r, (p << 1 | 1), a, b);
  else
    Delete(l, mid, (p << 1), a, b), Delete(mid + 1, r, (p << 1 | 1), a, b);
  Tr[p] = Tr[(p << 1)] + Tr[(p << 1 | 1)];
}
int GetF(int x) {
  Node d = (Node){0, 0};
  while (Top[x]) {
    d = Query(1, n, 1, ID[Top[x]], ID[x]) + d;
    x = Fa[Top[x]];
  }
  return d.f;
}
void Solve() {
  DFS(1);
  RDFS(1, 1);
  Build(1, n, 1);
  while (m--) {
    int Op, x;
    Rd(Op), Rd(x);
    if (Op == 1)
      Updata(1, n, 1, ID[x]);
    else if (Op == 2) {
      Delete(1, n, 1, ID[x], Ed[x]);
      int Tmp = GetF(Fa[x]);
      if (Tmp > 1) Updata(1, n, 1, ID[x], Tmp - 1);
    } else
      puts(GetF(x) > 0 ? "black" : "white");
  }
}
};  // namespace P2
int main() {
  Rd(n), Rd(m);
  for (int i = (2), i_end_ = (n + 1); i < i_end_; ++i) {
    Rd(Fa[i]);
    Add_Edge(Fa[i], i);
  }
  P2::Solve();
  return 0;
}
