Problem: Abendsen assigned a mission to Juliana. In this mission, Juliana has a rooted tree with n vertices. Vertex number 1 is the root of this tree. Each vertex can be either black or white. At first, all vertices are white. Juliana is asked to process q queries. Each query is one of three types:

  1. If vertex v is white, mark it as black; otherwise, perform this operation on all direct sons of v instead. 
  2. Mark all vertices in the subtree of v (including v) as white. 
  3. Find the color of the i-th vertex. 

<image> An example of operation "1 1" (corresponds to the first example test). The vertices 1 and 2 are already black, so the operation goes to their sons instead.

Can you help Juliana to process all these queries?

Input

The first line contains two integers n and q (2≤ n≤ 10^5, 1≤ q≤ 10^5) — the number of vertices and the number of queries.

The second line contains n-1 integers p_2, p_3, …, p_n (1≤ p_i<i), where p_i means that there is an edge between vertices i and p_i. 

Each of the next q lines contains two integers t_i and v_i (1≤ t_i≤ 3, 1≤ v_i≤ n) — the type of the i-th query and the vertex of the i-th query.

It is guaranteed that the given graph is a tree.

Output

For each query of type 3, print "black" if the vertex is black; otherwise, print "white".

Examples

Input

8 10
1 2 1 2 5 4 5
1 2
3 2
3 1
1 1
1 1
3 5
3 7
3 4
2 2
3 5


Output

black
white
black
white
black
white


Input

8 11
1 1 2 3 3 6 6
1 1
1 1
1 3
3 2
3 4
3 6
3 7
2 3
1 6
3 7
3 6


Output

black
white
black
white
white
black

Note

The first example is shown on the picture below.

<image>

The second example is shown on the picture below.

<image>

Solution (in CPP):

Solution:
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC target( \
    "sse,sse2,sse3,ssse3,sse4,sse4.1,sse4.2,popcnt,abm,mmx,avx,tune=native")
using namespace std;
const int MOD = 1000000007;
const int UNDEF = -1;
const int INF = 1 << 30;
template <typename T>
inline bool chkmax(T& aa, T bb) {
  return aa < bb ? aa = bb, true : false;
}
template <typename T>
inline bool chkmin(T& aa, T bb) {
  return aa > bb ? aa = bb, true : false;
}
int rint();
char rch();
long long rlong();
const int mn = 1e5 + 4;
int inp[mn];
class HeavyLight2NoRecursion {
 public:
  bool VALUES_ON_VERTICES = true;
  pair<int, int> modifyOperation(pair<int, int> x, int y) {
    return make_pair(x.first + y, x.second + y);
  }
  pair<int, int> queryOperation(pair<int, int> l, pair<int, int> r) {
    if (l == getNeutralValue()) return r;
    if (r == getNeutralValue()) return l;
    return make_pair(l.first + r.first, max(r.second, l.second + r.first));
  }
  int deltaEffectOnSegment(int delta, int segmentLength) { return delta; }
  int initNeutralDelta() { return 0; }
  bool isNeutralDelta(int delta) { return delta == 0; }
  pair<int, int> getInitValue(int vtx) { return make_pair(-1, -1); }
  pair<int, int> getNeutralValue() { return make_pair(INT_MIN, INT_MIN); }
  int joinDeltas(int d1, int d2) { return d1 + d2; }
  pair<int, int> joinValueWithDelta(pair<int, int> value, int delta) {
    if (isNeutralDelta(delta)) return value;
    return modifyOperation(value, delta);
  }
  int _cache_neutral_delta;
  int getNeutralDelta() { return _cache_neutral_delta; }
  pair<int, int>** vvalue;
  int** vdelta;
  int** len;
  vector<vector<int> > tree;
  int* size;
  int* parent;
  int* tin;
  int* tout;
  int time = 0;
  int* vpath;
  int* pathSize;
  int* pathPos;
  int* pathRoot;
  int pathCount = 0;
  void init(vector<vector<int> > tree) {
    _cache_neutral_delta = initNeutralDelta();
    this->tree = tree;
    int n = (int)tree.size();
    size = new int[n];
    parent = new int[n];
    tin = new int[n];
    tout = new int[n];
    calcSizeParentTinTout(0);
    vpath = new int[n];
    pathSize = new int[n];
    fill(pathSize, pathSize + n, 0);
    pathPos = new int[n];
    pathRoot = new int[n];
    buildPaths(0);
    vvalue = new pair<int, int>*[pathCount];
    vdelta = new int*[pathCount];
    len = new int*[pathCount];
    for (int i = 0; i < pathCount; i++) {
      int m = pathSize[i];
      vvalue[i] = new pair<int, int>[2 * m];
    }
    initInput();
  }
  void initInput() {
    int n = (int)this->tree.size();
    for (int vtx = 0; vtx < n; vtx++) {
      int path_idx = vpath[vtx];
      int m = pathSize[path_idx];
      int j = pathPos[vtx];
      ;
      vvalue[path_idx][j + m] = getInitValue(vtx);
    }
    for (int i = 0; i < pathCount; i++) {
      int m = pathSize[i];
      for (int j = 2 * m - 1; j > 1; j -= 2)
        vvalue[i][j >> 1] = queryOperation(vvalue[i][j], vvalue[i][j ^ 1]);
      vdelta[i] = new int[2 * m];
      for (int j = 0; j < 2 * m; j++) vdelta[i][j] = getNeutralDelta();
      len[i] = new int[2 * m];
      fill(len[i] + m, len[i] + (2 * m), 1);
      for (int j = 2 * m - 1; j > 1; j -= 2)
        len[i][j >> 1] = len[i][j] + len[i][j ^ 1];
    }
  }
  void calcSizeParentTinTout(int root) {
    int n = (int)tree.size();
    int* curEdge = new int[n];
    fill(curEdge, curEdge + n, 0);
    int* _stack = new int[n];
    _stack[0] = root;
    parent[root] = -1;
    for (int top = 0; top >= 0;) {
      int u = _stack[top];
      if (curEdge[u] == 0) {
        tin[u] = time++;
        size[u] = 1;
      }
      if (curEdge[u] < (int)tree[u].size()) {
        int v = tree[u][curEdge[u]++];
        if (curEdge[v] == 0) {
          _stack[++top] = v;
          parent[v] = u;
        }
      } else {
        --top;
        if (parent[u] != -1) size[parent[u]] += size[u];
        tout[u] = time++;
      }
    }
  }
  int newPath(int u) {
    pathRoot[pathCount] = u;
    return pathCount++;
  }
  void buildPaths(int root) {
    int n = (int)tree.size();
    int* curEdge = new int[n];
    fill(curEdge, curEdge + n, 0);
    int* _stackPath = new int[n];
    int* _stack = new int[n];
    _stack[0] = root;
    _stackPath[0] = newPath(root);
    for (int top = 0; top >= 0;) {
      int u = _stack[top];
      int path = _stackPath[top];
      if (curEdge[u] == 0) {
        this->vpath[u] = path;
        pathPos[u] = pathSize[path]++;
      }
      if (curEdge[u] < (int)tree[u].size()) {
        int v = tree[u][curEdge[u]++];
        if (curEdge[v] == 0) {
          _stack[++top] = v;
          _stackPath[top] = 2 * size[v] >= size[u] ? path : newPath(v);
        }
      } else {
        --top;
      }
    }
  }
  void buildPaths(int u, int path) {
    this->vpath[u] = path;
    pathPos[u] = pathSize[path]++;
    for (int v : tree[u]) {
      if (v != parent[u])
        buildPaths(v, 2 * size[v] >= size[u] ? path : newPath(v));
    }
  }
  void pushDelta(int path, int i) {
    int d = 0;
    for (; (i >> d) > 0; d++)
      ;
    for (d -= 2; d >= 0; d--) {
      int x = i >> d;
      vvalue[path][x >> 1] = joinValueWithDelta0(path, x >> 1);
      vdelta[path][x] = joinDeltas(vdelta[path][x], vdelta[path][x >> 1]);
      vdelta[path][x ^ 1] =
          joinDeltas(vdelta[path][x ^ 1], vdelta[path][x >> 1]);
      vdelta[path][x >> 1] = getNeutralDelta();
    }
  }
  pair<int, int> joinValueWithDelta0(int path, int i) {
    ;
    return joinValueWithDelta(
        vvalue[path][i], deltaEffectOnSegment(vdelta[path][i], len[path][i]));
  }
  pair<int, int> queryPath(int path, int from, int to) {
    ;
    from += pathSize[path];
    to += pathSize[path];
    pushDelta(path, from);
    pushDelta(path, to);
    pair<int, int> resl = getNeutralValue(), resr = getNeutralValue();
    bool found = false;
    for (int len = 1; from <= to;
         from = (from + 1) >> 1, to = (to - 1) >> 1, len <<= 1) {
      if ((from & 1) != 0) {
        resl = found ? queryOperation(resl, joinValueWithDelta0(path, from))
                     : joinValueWithDelta0(path, from);
        found = true;
      }
      if ((to & 1) == 0) {
        resr = found ? queryOperation(joinValueWithDelta0(path, to), resr)
                     : joinValueWithDelta0(path, to);
        found = true;
      }
    }
    pair<int, int> res = queryOperation(resl, resr);
    ;
    return res;
  }
  void modifyPath(int path, int from, int to, int delta) {
    ;
    from += pathSize[path];
    to += pathSize[path];
    pushDelta(path, from);
    pushDelta(path, to);
    int ta = -1;
    int tb = -1;
    for (; from <= to; from = (from + 1) >> 1, to = (to - 1) >> 1) {
      if ((from & 1) != 0) {
        this->vdelta[path][from] = joinDeltas(this->vdelta[path][from], delta);
        if (ta == -1) ta = from;
      }
      if ((to & 1) == 0) {
        this->vdelta[path][to] = joinDeltas(this->vdelta[path][to], delta);
        if (tb == -1) tb = to;
      }
    }
    for (int i = ta; i > 1; i >>= 1) {
      int k = i >> 1;
      vvalue[path][k] = queryOperation(joinValueWithDelta0(path, k << 1),
                                       joinValueWithDelta0(path, (k << 1) | 1));
    }
    for (int i = tb; i > 1; i >>= 1) {
      int k = i >> 1;
      vvalue[path][k] = queryOperation(joinValueWithDelta0(path, k << 1),
                                       joinValueWithDelta0(path, (k << 1) | 1));
    }
  }
  bool isAncestor(int p, int ch) {
    return tin[p] <= tin[ch] && tout[ch] <= tout[p];
  }
  pair<int, int> query(int a, int b) {
    ;
    pair<int, int> res = getNeutralValue();
    for (int root; !isAncestor(root = pathRoot[vpath[a]], b); a = parent[root])
      res = queryOperation(res, queryPath(vpath[a], 0, pathPos[a]));
    for (int root; !isAncestor(root = pathRoot[vpath[b]], a); b = parent[root])
      res = queryOperation(queryPath(vpath[b], 0, pathPos[b]), res);
    if (!VALUES_ON_VERTICES && a == b) return res;
    return queryOperation(
        queryPath(vpath[a],
                  min(pathPos[a], pathPos[b]) + (VALUES_ON_VERTICES ? 0 : 1),
                  max(pathPos[a], pathPos[b])),
        res);
  }
  void modify(int a, int b, int delta) {
    for (int root; !isAncestor(root = pathRoot[vpath[a]], b); a = parent[root])
      modifyPath(vpath[a], 0, pathPos[a], delta);
    for (int root; !isAncestor(root = pathRoot[vpath[b]], a); b = parent[root])
      modifyPath(vpath[b], 0, pathPos[b], delta);
    if (!VALUES_ON_VERTICES && a == b) return;
    modifyPath(vpath[a],
               min(pathPos[a], pathPos[b]) + (VALUES_ON_VERTICES ? 0 : 1),
               max(pathPos[a], pathPos[b]), delta);
  }
};
HeavyLight2NoRecursion hl;
vector<vector<int> > g;
vector<int> v2q[mn];
pair<int, int> q2tv[mn];
vector<int> q2undo[mn];
pair<int, int> q2op[mn];
set<int> s;
void dfs(int v, int p) {
  for (auto& q : v2q[v]) {
    if (q2tv[q].first == 3) continue;
    auto it = s.lower_bound(q);
    if (it != s.end()) {
      q2undo[*it].push_back(q);
    }
    if (q2tv[q].first == 2) s.insert(q);
  }
  for (auto& y : g[v]) {
    if (y != p) dfs(y, v);
  }
  for (auto& q : v2q[v])
    if (q2tv[q].first == 2) s.erase(q);
}
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  int n = rint();
  int qn = rint();
  g.resize(n);
  for (int i = 2; i <= n; i++) {
    int x = i;
    int y = rint();
    --x;
    --y;
    g[x].push_back(y);
    g[y].push_back(x);
  }
  for (int qi = 0; qi < qn; qi++) {
    int t = rint();
    int v = rint() - 1;
    q2tv[qi] = make_pair(t, v);
    v2q[v].push_back(qi);
  }
  dfs(0, -1);
  for (int x = 0; x < qn; x++) sort(q2undo[x].begin(), q2undo[x].end());
  hl.init(g);
  for (int q = 0; q < qn; q++) {
    int t = q2tv[q].first, v = q2tv[q].second;
    if (t == 1) {
      q2op[q] = (make_pair(v, 1));
      hl.modify(v, v, 1);
    } else if (t == 2) {
      for (auto& u : q2undo[q]) {
        auto w = q2op[u];
        if (w.second != 0) {
          hl.modify(w.first, w.first, -w.second);
        }
      }
      pair<int, int> got = hl.query(0, v);
      int plus = got.second - (-1);
      if (plus > 0) {
        q2op[q] = (make_pair(v, -plus));
        hl.modify(v, v, -plus);
      }
    } else {
      pair<int, int> got = hl.query(0, v);
      if (got.second >= 0)
        printf("black\n");
      else
        printf("white\n");
    }
  }
}
static char stdinBuffer[1024];
static char* stdinDataEnd = stdinBuffer + sizeof(stdinBuffer);
static const char* stdinPos = stdinDataEnd;
void readAhead(size_t amount) {
  size_t remaining = stdinDataEnd - stdinPos;
  if (remaining < amount) {
    memmove(stdinBuffer, stdinPos, remaining);
    size_t sz = fread(stdinBuffer + remaining, 1,
                      sizeof(stdinBuffer) - remaining, stdin);
    stdinPos = stdinBuffer;
    stdinDataEnd = stdinBuffer + remaining + sz;
    if (stdinDataEnd != stdinBuffer + sizeof(stdinBuffer)) *stdinDataEnd = 0;
  }
}
int rint() {
  readAhead(16);
  int x = 0;
  bool neg = false;
  while (*stdinPos == ' ' || *stdinPos == '\n') ++stdinPos;
  if (*stdinPos == '-') {
    ++stdinPos;
    neg = true;
  }
  while (*stdinPos >= '0' && *stdinPos <= '9') {
    x *= 10;
    x += *stdinPos - '0';
    ++stdinPos;
  }
  return neg ? -x : x;
}
char rch() {
  readAhead(16);
  while (*stdinPos == ' ' || *stdinPos == '\n') ++stdinPos;
  char ans = *stdinPos;
  ++stdinPos;
  return ans;
}
long long rlong() {
  readAhead(32);
  long long x = 0;
  bool neg = false;
  while (*stdinPos == ' ' || *stdinPos == '\n') ++stdinPos;
  if (*stdinPos == '-') {
    ++stdinPos;
    neg = true;
  }
  while (*stdinPos >= '0' && *stdinPos <= '9') {
    x *= 10;
    x += *stdinPos - '0';
    ++stdinPos;
  }
  return neg ? -x : x;
}