#include <bits/stdc++.h>
using namespace std;

const int N = 1.5e5;
const int A = 26;

vector<int> adj[N];
int height[N];
int node[N];
int par[N];

char color[N];
int cnt[N][A];
int mx[N][A];
bool ok[N];

int m;

void dfs_height(int u) {
  height[u] = 0;
  for (int c : adj[u]) {
    dfs_height(c);
    if (height[u] == 0) {
      height[u] = height[c] + 1;
    } else if (height[u] != height[c] + 1) {
      for (int i = 0; i < m; ++i) {
        cout << "Fou\n";
      }
      exit(0);
    }
  }
}

void pull(int u) {
  ok[u] = true;
  fill(mx[u], mx[u] + A, 0);
  for (int c : adj[u]) {
    ok[u] &= ok[c];
    for (int i = 0; i < A; ++i) {
      mx[u][i] = max(mx[u][i], mx[c][i]);
    }
  }
  ok[u] &= (accumulate(mx[u], mx[u] + A, 0) <= height[u]);

  for (int i = 0; i < A; ++i) {
    mx[u][i] += cnt[u][i];
  }
}

int dfs(int u) {
  vector<int> path;
  while (adj[u].size() == 1u) {
    path.push_back(u);
    u = adj[u][0];
  }

  node[u] = u;
  for (auto v : path) {
    node[v] = u;
    for (int i = 0; i < A; ++i) {
      cnt[u][i] += cnt[v][i];
    }
  }

  for (auto& c : adj[u]) {
    c = dfs(c);
    par[c] = u;
  }
  pull(u);
  return u;
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);

  int n;
  cin >> n >> m;

  for (int i = 2; i <= n; ++i) {
    int p;
    cin >> p >> color[i];
    adj[p].push_back(i);
    if (color[i] >= 'a') {
      ++cnt[i][color[i] - 'a'];
    }
  }

  dfs_height(1);
  int root = dfs(1);

  while (m--) {
    int u;
    cin >> u;
    if (color[u] >= 'a') {
      --cnt[node[u]][color[u] - 'a'];
    }

    cin >> color[u];
    if (color[u] >= 'a') {
      ++cnt[node[u]][color[u] - 'a'];
    }

    u = node[u];
    while (u != 0) {
      pull(u);
      u = par[u];
    }

    if (ok[root]) {
      int sum = (height[1] - accumulate(mx[root], mx[root] + A, 0)) * 13 * 27;
      for (int i = 0; i < A; ++i) {
        sum += mx[root][i] * (i + 1);
      }
      cout << "Shi " << sum << "\n";
    } else {
      cout << "Fou\n";
    }
  }
}


#include <bits/stdc++.h>
using namespace std;

const int N = 1.5e5 + 1;
const int A = 26;

vector<int> adj[N];
int height[N];
int node[N];
int par[N];

char color[N];
int cnt[N][A];
int mx[N][A];
bool ok[N];

int m;

void dfs_height(int u) {
  height[u] = 0;
  for (int c : adj[u]) {
    dfs_height(c);
    if (height[u] == 0) {
      height[u] = height[c] + 1;
    } else if (height[u] != height[c] + 1) {
      for (int i = 0; i < m; ++i) {
        cout << "Fou\n";
      }
      exit(0);
    }
  }
}

void pull(int u) {
  ok[u] = true;
  fill(mx[u], mx[u] + A, 0);
  for (int c : adj[u]) {
    ok[u] &= ok[c];
    for (int i = 0; i < A; ++i) {
      mx[u][i] = max(mx[u][i], mx[c][i]);
    }
  }
  ok[u] &= (accumulate(mx[u], mx[u] + A, 0) <= height[u]);

  for (int i = 0; i < A; ++i) {
    mx[u][i] += cnt[u][i];
  }
}

int dfs(int u) {
  vector<int> path;
  while (adj[u].size() == 1u) {
    path.push_back(u);
    u = adj[u][0];
  }

  node[u] = u;
  for (auto v : path) {
    node[v] = u;
    for (int i = 0; i < A; ++i) {
      cnt[u][i] += cnt[v][i];
    }
  }

  for (auto& c : adj[u]) {
    c = dfs(c);
    par[c] = u;
  }
  pull(u);
  return u;
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);

  int n;
  cin >> n >> m;

  for (int i = 2; i <= n; ++i) {
    int p;
    cin >> p >> color[i];
    adj[p].push_back(i);
    if (color[i] >= 'a') {
      ++cnt[i][color[i] - 'a'];
    }
  }

  dfs_height(1);
  int root = dfs(1);

  while (m--) {
    int u;
    cin >> u;
    if (color[u] >= 'a') {
      --cnt[node[u]][color[u] - 'a'];
    }

    cin >> color[u];
    if (color[u] >= 'a') {
      ++cnt[node[u]][color[u] - 'a'];
    }

    u = node[u];
    while (u != 0) {
      pull(u);
      u = par[u];
    }

    if (ok[root]) {
      int sum = (height[1] - accumulate(mx[root], mx[root] + A, 0)) * 13 * 27;
      for (int i = 0; i < A; ++i) {
        sum += mx[root][i] * (i + 1);
      }
      cout << "Shi " << sum << "\n";
    } else {
      cout << "Fou\n";
    }
  }
}


#include <bits/stdc++.h>
using namespace std;

const int N = 1.5e5;
const int A = 26;

vector<int> adj[N];
int height[N];
int node[N];
int par[N];

char color[N];
int cnt[N][A];
int mx[N][A];
bool ok[N];

int m;

void dfs_height(int u) {
  height[u] = 1;
  for (int c : adj[u]) {
    dfs_height(c);
    if (height[u] == 1) {
      height[u] = height[c] + 1;
    } else if (height[u] != height[c] + 1) {
      for (int i = 0; i < m; ++i) {
        cout << "Fou\n";
      }
      exit(0);
    }
  }
}

void pull(int u) {
  ok[u] = true;
  fill(mx[u], mx[u] + A, 0);
  for (int c : adj[u]) {
    ok[u] &= ok[c];
    for (int i = 0; i < A; ++i) {
      mx[u][i] = max(mx[u][i], mx[c][i]);
    }
  }

  for (int i = 0; i < A; ++i) {
    mx[u][i] += cnt[u][i];
  }
  ok[u] &= (accumulate(mx[u], mx[u] + A, 0) <= height[u]);
}

void dfs(int u) {
  int v = u;
  node[u] = u;
  while (adj[v].size() == 1u) {
    v = adj[v][0];
    for (int i = 0; i < A; ++i) {
      cnt[u][i] += cnt[v][i];
    }
    node[v] = u;
  }
  adj[u] = adj[v];

  for (int c : adj[u]) {
    par[c] = u;
    dfs(c);
  }
  pull(u);
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);

  int n;
  cin >> n >> m;

  for (int i = 2; i <= n; ++i) {
    int p;
    cin >> p >> color[i];
    adj[p].push_back(i);
    if (color[i] >= 'a') {
      ++cnt[i][color[i] - 'a'];
    }
  }

  dfs_height(1);
  --height[1];
  dfs(1);

  while (m--) {
    int u;
    cin >> u;
    if (color[u] >= 'a') {
      --cnt[node[u]][color[u] - 'a'];
    }

    cin >> color[u];
    if (color[u] >= 'a') {
      ++cnt[node[u]][color[u] - 'a'];
    }

    u = node[u];
    while (u != 0) {
      pull(u);
      u = par[u];
    }

    if (ok[1]) {
      int sum = (height[1] - accumulate(mx[1], mx[1] + A, 0)) * 13 * 27;
      for (int i = 0; i < A; ++i) {
        sum += mx[1][i] * (i + 1);
      }
      cout << "Shi " << sum << "\n";
    } else {
      cout << "Fou\n";
    }
  }
}


#include <bits/stdc++.h>
using namespace std;

const int N = 1.5e5;
const int A = 26;

vector<int> adj[N];
int height[N];
int node[N];
int par[N];

char color[N];
int cnt[N][A];
int mx[N][A];
bool ok[N];

int m;

void dfs_height(int u) {
  height[u] = 1;
  for (int c : adj[u]) {
    dfs_height(c);
    if (height[u] == 1) {
      height[u] = height[c] + 1;
    } else if (height[u] != height[c] + 1) {
      for (int i = 0; i < m; ++i) {
        cout << "Fou\n";
      }
      exit(0);
    }
  }
}

void pull(int u) {
  ok[u] = true;
  fill(mx[u], mx[u] + A, 0);
  for (int c : adj[u]) {
    ok[u] &= ok[c];
    for (int i = 0; i < A; ++i) {
      mx[u][i] = max(mx[u][i], mx[c][i]);
    }
  }

  for (int i = 0; i < A; ++i) {
    mx[u][i] += cnt[u][i];
  }
  ok[u] &= (accumulate(mx[u], mx[u] + A, 0) <= height[u]);
}

void dfs(int u) {
  int v = u;
  node[u] = u;
  while (adj[v].size() == 1u) {
    v = adj[u][0];
    for (int i = 0; i < A; ++i) {
      cnt[u][i] += cnt[v][i];
    }
    node[v] = u;
  }
  adj[u] = adj[v];

  for (int c : adj[u]) {
    par[c] = u;
    dfs(c);
  }
  pull(u);
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);

  int n;
  cin >> n >> m;

  for (int i = 2; i <= n; ++i) {
    int p;
    cin >> p >> color[i];
    adj[p].push_back(i);
    if (color[i] >= 'a') {
      ++cnt[i][color[i] - 'a'];
    }
  }

  dfs_height(1);
  --height[1];
  dfs(1);

  while (m--) {
    int u;
    cin >> u;
    if (color[u] >= 'a') {
      --cnt[node[u]][color[u] - 'a'];
    }

    cin >> color[u];
    if (color[u] >= 'a') {
      ++cnt[node[u]][color[u] - 'a'];
    }

    u = node[u];
    while (u != 0) {
      pull(u);
      u = par[u];
    }

    if (ok[1]) {
      int sum = (height[1] - accumulate(mx[1], mx[1] + A, 0)) * 13 * 27;
      for (int i = 0; i < A; ++i) {
        sum += mx[1][i] * (i + 1);
      }
      cout << "Shi " << sum << "\n";
    } else {
      cout << "Fou\n";
    }
  }
}


/* cerberus97 - Hanit Banga */

#include <iostream>
#include <iomanip>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

#define pb push_back
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL)

typedef long long ll;
typedef long double ld;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const int N = 15e4 + 10, C = 26;

vector<int> g[N];
int par[N], upd[N], id[N], depth[N], sum[N];
int letters[N][C], ctr[N][C][2];
bool ok[N];
char act_letter[N];
set<int> depths;
int common_depth, num_ok = 0;

void dfs1(int u);
void dfs2(int u);
void update(int u, int c, int x);

int main() {
	fast_cin();
	int n, q;
	cin >> n >> q;
	upd[1] = 1;
	for (int i = 2; i <= n; ++i) {
		int p; char c;
		cin >> p >> c;
		if (!g[p].empty()) {
			id[i] = 1;
		}
		g[p].pb(i);
		par[i] = p;
		upd[i] = i;
		if (c != '?') {
			letters[i][c - 'a']++;
		}
		act_letter[i] = c;
	}
	dfs1(1);
	if (depths.size() > 1) {
		for (int i = 0; i < q; ++i) {
			cout << "Fou\n";
		}
		return 0;
	}
	common_depth = *depths.begin();
	dfs2(1);
	// for (int i = 1; i <= n; ++i) {
	// 	if (upd[i] == i) {
	// 		cout << i << ' ' << par[i] << endl;
	// 	} else {
	// 		cout << i << ' ' << "DEL" << ' ' << upd[i] << endl;
	// 	}
	// }
	while (q--) {
		int v; char c;
		cin >> v >> c;
		int u = upd[v];
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', -1);
		}
		act_letter[v] = c;
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', 1);
		}
		if (num_ok == n) {
			cout << "Shi ";
			ll ans = 0;
			for (int i = 0; i < C; ++i) {
				ans += max(ctr[1][i][0], ctr[1][i][1]) * (i + 1);
			}
			int extra = common_depth - sum[1];
			for (int i = 0; i < C; ++i) {
				ans += extra * (i + 1);
			}
			cout << ans << '\n';
		} else {
			cout << "Fou\n";
		}
	}
}

void dfs1(int u) {
	if (g[u].empty()) {
		depths.insert(depth[u]);
		return;
	}
	for (auto &v : g[u]) {
		depth[v] = depth[u] + 1;
		dfs1(v);
	}
}

void dfs2(int u) {
	for (auto &v : g[u]) {
		dfs2(v);
	}
	if (upd[u] != u) {
		// cout << u << ' ' << upd[u] << endl;
		++num_ok;
		return;
	}
	int p = par[u];
	while (p > 1 and g[p].size() == 1) {
		for (int j = 0; j < C; ++j) {
			letters[u][j] += letters[p][j];
		}
		upd[p] = u;
		id[u] = id[p];
		p = par[p];
		par[u] = p;
	}
	int have = common_depth - depth[u];
	for (int j = 0; j < C; ++j) {
		sum[u] += max(ctr[u][j][0], ctr[u][j][1]);
	}
	if (sum[u] <= have) {
		ok[u] = true;
		++num_ok;
	}
	if (u == 1) {
		return;
	}
	// int p = par[u];
	for (int j = 0; j < C; ++j) {
		ctr[p][j][id[u]] = max(ctr[u][j][0], ctr[u][j][1]) + letters[u][j];
	}
}

void update(int u, int c, int x) {
	letters[u][c] += x;
	while (u != 1) {
		int p = par[u];
		// for (int j = 0; j < C; ++j) {
		sum[p] -= max(ctr[p][c][0], ctr[p][c][1]);
		ctr[p][c][id[u]] = max(ctr[u][c][0], ctr[u][c][1]) + letters[u][c];
		sum[p] += max(ctr[p][c][0], ctr[p][c][1]);
		// }
		u = p;
		int have = common_depth - depth[u];
		if (sum[u] <= have and !ok[u]) {
			ok[u] = true;
			++num_ok;
		} else if (sum[u] > have and ok[u]) {
			ok[u] = false;
			--num_ok;
		}
	}
}


/* cerberus97 - Hanit Banga */

#include <iostream>
#include <iomanip>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

#define pb push_back
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL)

typedef long long ll;
typedef long double ld;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const int N = 15e4 + 10, C = 26;

vector<int> g[N];
int par[N], upd[N], id[N], depth[N];
int letters[N][C], ctr[N][C][2];
bool ok[N];
char act_letter[N];
set<int> depths;
int common_depth, num_ok = 0;

void dfs1(int u);
void dfs2(int u);
void update(int u, int c, int x);

int main() {
	fast_cin();
	int n, q;
	cin >> n >> q;
	upd[1] = 1;
	for (int i = 2; i <= n; ++i) {
		int p; char c;
		cin >> p >> c;
		if (!g[p].empty()) {
			id[i] = 1;
		}
		g[p].pb(i);
		par[i] = p;
		upd[i] = i;
		if (c != '?') {
			letters[i][c - 'a']++;
		}
		act_letter[i] = c;
	}
	dfs1(1);
	if (depths.size() > 1) {
		for (int i = 0; i < q; ++i) {
			cout << "Fou\n";
		}
		return 0;
	}
	common_depth = *depths.begin();
	dfs2(1);
	while (q--) {
		int v; char c;
		cin >> v >> c;
		int u = upd[v];
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', -1);
		}
		act_letter[v] = c;
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', 1);
		}
		if (num_ok == n) {
			cout << "Shi ";
			ll ans = 0;
			int sum = 0;
			for (int i = 0; i < C; ++i) {
				ans += max(ctr[1][i][0], ctr[1][i][1]) * (i + 1);
				sum += max(ctr[1][i][0], ctr[1][i][1]);
			}
			int extra = common_depth - sum;
			for (int i = 0; i < C; ++i) {
				ans += extra * (i + 1);
			}
			cout << ans << '\n';
		} else {
			cout << "Fou\n";
		}
	}
}

void dfs1(int u) {
	if (g[u].empty()) {
		depths.insert(depth[u]);
		return;
	}
	for (auto &v : g[u]) {
		depth[v] = depth[u] + 1;
		dfs1(v);
	}
}

void dfs2(int u) {
	for (auto &v : g[u]) {
		dfs2(v);
	}
	if (upd[u] != u) {
		// cout << u << ' ' << upd[u] << endl;
		++num_ok;
		return;
	}
	if (g[u].size() == 1) {
		int p = par[u];
		while (p > 1 and g[p].size() == 1) {
			for (int j = 0; j < C; ++j) {
				letters[u][j] += letters[p][j];
			}
			upd[p] = u;
			id[u] = id[p];
			p = par[p];
			par[u] = p;
		}
	}
	int sum = 0, have = common_depth - depth[u];
	for (int j = 0; j < C; ++j) {
		sum += max(ctr[u][j][0], ctr[u][j][1]);
	}
	if (sum <= have) {
		ok[u] = true;
		++num_ok;
	}
	if (u == 1) {
		return;
	}
	int p = par[u];
	for (int j = 0; j < C; ++j) {
		ctr[p][j][id[u]] = max(ctr[u][j][0], ctr[u][j][1]) + letters[u][j];
	}
}

void update(int u, int c, int x) {
	letters[u][c] += x;
	while (u != 1) {
		int p = par[u];
		ctr[p][c][id[u]] += x;
		u = p;
		int sum = 0, have = common_depth - depth[u];
		for (int j = 0; j < C; ++j) {
			sum += max(ctr[u][j][0], ctr[u][j][1]);
		}
		if (sum <= have and !ok[u]) {
			ok[u] = true;
			++num_ok;
		} else if (sum > have and ok[u]) {
			ok[u] = false;
			--num_ok;
		}
	}
}


/* cerberus97 - Hanit Banga */

#include <iostream>
#include <iomanip>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

#define pb push_back
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL)

typedef long long ll;
typedef long double ld;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const int N = 15e4 + 10, C = 26;

vector<int> g[N];
int par[N], upd[N], id[N], depth[N];
int letters[N][C], ctr[N][C][2];
bool ok[N];
char act_letter[N];
set<int> depths;
int common_depth, num_ok = 0;

void dfs1(int u);
void dfs2(int u);
void update(int u, int c, int x);

int main() {
	fast_cin();
	int n, q;
	cin >> n >> q;
	upd[1] = 1;
	for (int i = 2; i <= n; ++i) {
		int p; char c;
		cin >> p >> c;
		if (!g[p].empty()) {
			id[i] = 1;
		}
		g[p].pb(i);
		par[i] = p;
		upd[i] = i;
		if (c != '?') {
			letters[i][c - 'a']++;
		}
		act_letter[i] = c;
	}
	dfs1(1);
	if (depths.size() > 1) {
		for (int i = 0; i < q; ++i) {
			cout << "Fou\n";
		}
		return 0;
	}
	common_depth = *depths.begin();
	dfs2(1);
	// for (int i = 1; i <= n; ++i) {
	// 	if (upd[i] == i) {
	// 		cout << i << ' ' << par[i] << endl;
	// 	} else {
	// 		cout << i << ' ' << "DEL" << ' ' << upd[i] << endl;
	// 	}
	// }
	while (q--) {
		int v; char c;
		cin >> v >> c;
		int u = upd[v];
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', -1);
		}
		act_letter[v] = c;
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', 1);
		}
		if (num_ok == n) {
			cout << "Shi ";
			ll ans = 0;
			int sum = 0;
			for (int i = 0; i < C; ++i) {
				ans += max(ctr[1][i][0], ctr[1][i][1]) * (i + 1);
				sum += max(ctr[1][i][0], ctr[1][i][1]);
			}
			int extra = common_depth - sum;
			for (int i = 0; i < C; ++i) {
				ans += extra * (i + 1);
			}
			cout << ans << '\n';
		} else {
			cout << "Fou\n";
		}
	}
}

void dfs1(int u) {
	if (g[u].empty()) {
		depths.insert(depth[u]);
		return;
	}
	for (auto &v : g[u]) {
		depth[v] = depth[u] + 1;
		dfs1(v);
	}
}

void dfs2(int u) {
	for (auto &v : g[u]) {
		dfs2(v);
	}
	if (upd[u] != u) {
		// cout << u << ' ' << upd[u] << endl;
		++num_ok;
		return;
	}
	int p = par[u];
	while (p > 1 and g[p].size() == 1) {
		for (int j = 0; j < C; ++j) {
			letters[u][j] += letters[p][j];
		}
		upd[p] = u;
		id[u] = id[p];
		p = par[p];
		par[u] = p;
	}
	int sum = 0, have = common_depth - depth[u];
	for (int j = 0; j < C; ++j) {
		sum += max(ctr[u][j][0], ctr[u][j][1]);
	}
	if (sum <= have) {
		ok[u] = true;
		++num_ok;
	}
	if (u == 1) {
		return;
	}
	// int p = par[u];
	for (int j = 0; j < C; ++j) {
		ctr[p][j][id[u]] = max(ctr[u][j][0], ctr[u][j][1]) + letters[u][j];
	}
}

void update(int u, int c, int x) {
	letters[u][c] += x;
	while (u != 1) {
		int p = par[u];
		for (int j = 0; j < C; ++j) {
			ctr[p][j][id[u]] = max(ctr[u][j][0], ctr[u][j][1]) + letters[u][j];
		}
		u = p;
		int sum = 0, have = common_depth - depth[u];
		for (int j = 0; j < C; ++j) {
			sum += max(ctr[u][j][0], ctr[u][j][1]);
		}
		if (sum <= have and !ok[u]) {
			ok[u] = true;
			++num_ok;
		} else if (sum > have and ok[u]) {
			ok[u] = false;
			--num_ok;
		}
	}
}


/* cerberus97 - Hanit Banga */

#include <iostream>
#include <iomanip>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

#define pb push_back
#define fast_cin() ios_base::sync_with_stdio(false); cin.tie(NULL)

typedef long long ll;
typedef long double ld;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const int N = 15e4 + 10, C = 26;

vector<int> g[N];
int par[N], upd[N], id[N], depth[N];
int letters[N][C], ctr[N][C][2];
bool ok[N];
char act_letter[N];
set<int> depths;
int common_depth, num_ok = 0;

void dfs1(int u);
void dfs2(int u);
void update(int u, int c, int x);

int main() {
	fast_cin();
	int n, q;
	cin >> n >> q;
	upd[1] = 1;
	for (int i = 2; i <= n; ++i) {
		int p; char c;
		cin >> p >> c;
		if (!g[p].empty()) {
			id[i] = 1;
		}
		g[p].pb(i);
		par[i] = p;
		upd[i] = i;
		if (c != '?') {
			letters[i][c - 'a']++;
		}
		act_letter[i] = c;
	}
	dfs1(1);
	if (depths.size() > 1) {
		for (int i = 0; i < q; ++i) {
			cout << "Fou\n";
		}
		return 0;
	}
	common_depth = *depths.begin();
	dfs2(1);
	// for (int i = 1; i <= n; ++i) {
	// 	if (upd[i] == i) {
	// 		cout << i << ' ' << par[i] << endl;
	// 	} else {
	// 		cout << i << ' ' << "DEL" << ' ' << upd[i] << endl;
	// 	}
	// }
	while (q--) {
		int v; char c;
		cin >> v >> c;
		int u = upd[v];
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', -1);
		}
		act_letter[v] = c;
		if (act_letter[v] != '?') {
			update(u, act_letter[v] - 'a', 1);
		}
		if (num_ok == n) {
			cout << "Shi ";
			ll ans = 0;
			int sum = 0;
			for (int i = 0; i < C; ++i) {
				ans += max(ctr[1][i][0], ctr[1][i][1]) * (i + 1);
				sum += max(ctr[1][i][0], ctr[1][i][1]);
			}
			int extra = common_depth - sum;
			for (int i = 0; i < C; ++i) {
				ans += extra * (i + 1);
			}
			cout << ans << '\n';
		} else {
			cout << "Fou\n";
		}
	}
}

void dfs1(int u) {
	if (g[u].empty()) {
		depths.insert(depth[u]);
		return;
	}
	for (auto &v : g[u]) {
		depth[v] = depth[u] + 1;
		dfs1(v);
	}
}

void dfs2(int u) {
	for (auto &v : g[u]) {
		dfs2(v);
	}
	if (upd[u] != u) {
		// cout << u << ' ' << upd[u] << endl;
		++num_ok;
		return;
	}
	int p = par[u];
	while (p > 1 and g[p].size() == 1) {
		for (int j = 0; j < C; ++j) {
			letters[u][j] += letters[p][j];
		}
		upd[p] = u;
		id[u] = id[p];
		p = par[p];
		par[u] = p;
	}
	int sum = 0, have = common_depth - depth[u];
	for (int j = 0; j < C; ++j) {
		sum += max(ctr[u][j][0], ctr[u][j][1]);
	}
	if (sum <= have) {
		ok[u] = true;
		++num_ok;
	}
	if (u == 1) {
		return;
	}
	// int p = par[u];
	for (int j = 0; j < C; ++j) {
		ctr[p][j][id[u]] = max(ctr[u][j][0], ctr[u][j][1]) + letters[u][j];
	}
}

void update(int u, int c, int x) {
	letters[u][c] += x;
	while (u != 1) {
		int p = par[u];
		ctr[p][c][id[u]] += x;
		u = p;
		int sum = 0, have = common_depth - depth[u];
		for (int j = 0; j < C; ++j) {
			sum += max(ctr[u][j][0], ctr[u][j][1]);
		}
		if (sum <= have and !ok[u]) {
			ok[u] = true;
			++num_ok;
		} else if (sum > have and ok[u]) {
			ok[u] = false;
			--num_ok;
		}
	}
}


#include <bits/stdc++.h>
#define fr first
#define sc second
#define MP make_pair
#define PB push_back
#define fastio ios_base::sync_with_stdio(false); cin.tie(0);
#define lli long long int
#define y1 arayikhalatyan
#define j1 jiglypuff
#define ld long double
using namespace std;

lli gcd(lli a, lli b) { return (b == 0 ? a : gcd(b, a % b)); }

char vow[] = { 'a', 'e', 'i', 'o', 'u' };
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };

const int N = 4e5 + 30;
const lli mod = -1e11;

int n, q;
int p[N], l[N], sum[N], xr[N], nerq[N], ln[N];
int a[N][30], dp[N][30];
int qan;
bool col[N], cl[N], bl;

vector <int> g[N];
void dfs(int v, int par)
{
    if(l[par] == 1)
    {
        for (int i = 0; i < 27; i++)
        {
            a[v][i] += a[par][i];
        }
        p[v] = p[par];
    }
    for(auto p1 : g[v])
    {
        dfs(p1, v);
    }
}
void upd(int v, int A, int B)
{
    if(v == 1)
    {
        return;
    }
    int par = p[v];
    //cout << v << " " << par << endl;
    if(A != 26)
        sum[par] -= dp[par][A];
    if(B != 26)
        sum[par] -= dp[par][B];
    dp[par][A] = dp[par][B] = 0;
    for(auto p : g[par])
    {
        p = nerq[p];
        //if(par == 1) cout << p << "<-";
        dp[par][A] = max(dp[par][A], dp[p][A] + a[p][A]);
        dp[par][B] = max(dp[par][B], dp[p][B] + a[p][B]);
    }
    if(A != 26)
        sum[par] += dp[par][A];
    if(B != 26)
        sum[par] += dp[par][B];
    if(sum[par] > xr[par] && !col[par]) qan++, col[par] = true;
    else if(sum[par] <= xr[par] && col[par]) qan--, col[par] = false;
    upd(par, A, B);
}
int main()
{
    fastio;
    cin >> n >> q;
    p[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        cin >> p[i];
        char A;
        cin >> A;
        if(A == '?') A = 26;
        else A -= 'a';
        a[i][A]++;
        ln[i] = A;
        l[p[i]]++;
        g[p[i]].PB(i);
    }
    for(int i = n; i >= 2; i--)
    {
        if(xr[p[i]] && xr[p[i]] != xr[i] + 1) bl = true;
        xr[p[i]] = xr[i] + 1;
        for (int j = 0; j < 27; j++)
        {
            dp[p[i]][j] = max(dp[p[i]][j], dp[i][j] + a[i][j]);
            sum[i] += dp[i][j];
        }
        sum[i] -= dp[i][26];
    }
    if(bl)
    {
        for (int i = 0; i < q; i++)
        {
            cout << "Fou\n";
        }
        return 0;
    }
    for (int i = 0; i < 26; i++)
    {
        sum[1] += dp[1][i];
    }
    for (int i = 1; i <= n; i++)
    {
        if(l[i] != 1) cl[i] = true;
        col[i] = xr[i] < sum[i];
        qan += col[i];
    }
    cl[1] = true;
    for(int i = n; i >= 1; i--)
    {
        if(!cl[i])
            nerq[i] = nerq[g[i][0]];
        else
            nerq[i] = i;
        //cout << nerq[i] << " ";
    }
    dfs(1, 1);
    for (int i = 0; i < q; i++)
    {
        int v;
        char A;
        cin >> v >> A;
        int sm1, sm2;
        if(A == '?') sm1 = 26;
        else sm1 = A - 'a';
        sm2 = ln[v];
        ln[v] = sm1;
        v = nerq[v];
        a[v][sm1]++;
        a[v][sm2]--;
        upd(v, sm1, sm2);
        //cout << dp[5]['w' - 'a'] << "|" << a[5]['w' - 'a'] << endl;
        if(qan)
        {
            cout << "Fou\n";
        }
        else
        {
            int sm = 0;
            //cout << sum[1] << endl;
            for (int i = 0; i < 26; i++)
            {
               // if(dp[1][i]) cout << char('a' + i) << "-" << dp[1][i] << "\n";
                sm += (i + 1) * (dp[1][i] + xr[1] - sum[1]);
            }
           // cout << "?-" << dp[1][26] << endl;
            cout <<  "Shi " << sm << endl;
        }
    }
    return 0;
}


#include <bits/stdc++.h>
#define fr first
#define sc second
#define MP make_pair
#define PB push_back
#define fastio ios_base::sync_with_stdio(false); cin.tie(0);
#define lli long long int
#define y1 arayikhalatyan
#define j1 jiglypuff
#define ld long double
using namespace std;

lli gcd(lli a, lli b) { return (b == 0 ? a : gcd(b, a % b)); }

char vow[] = { 'a', 'e', 'i', 'o', 'u' };
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };

const int N = 4e5 + 30;
const lli mod = -1e11;

int n, q;
int p[N], l[N], sum[N], xr[N], nerq[N], ln[N];
int a[N][30], dp[N][30];
int qan;
bool col[N], cl[N], bl;

vector <int> g[N];
void dfs(int v, int par)
{
    if(l[par] == 1)
    {
        for (int i = 0; i < 27; i++)
        {
            a[v][i] += a[par][i];
        }
        p[v] = p[par];
    }
    for(auto p1 : g[v])
    {
        dfs(p1, v);
    }
}
void upd(int v, int A, int B)
{
    if(v == 1)
    {
        return;
    }
    int par = p[v];
    if(A != 26)
        sum[par] -= dp[par][A];
    if(B != 26)
        sum[par] -= dp[par][B];
    dp[par][A] = dp[par][B] = 0;
    for(auto p : g[par])
    {
        p = nerq[p];
        dp[par][A] = max(dp[par][A], dp[p][A] + a[p][A]);
        dp[par][B] = max(dp[par][B], dp[p][B] + a[p][B]);
    }
    if(A != 26)
        sum[par] += dp[par][A];
    if(B != 26)
        sum[par] += dp[par][B];
    if(sum[par] > xr[par] && !col[par]) qan++, col[par] = true;
    else if(sum[par] <= xr[par] && col[par]) qan--, col[par] = false;
}
int main()
{
    fastio;
    cin >> n >> q;
    p[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        cin >> p[i];
        char A;
        cin >> A;
        if(A == '?') A = 26;
        else A -= 'a';
        a[i][A]++;
        ln[i] = A;
        l[p[i]]++;
        g[p[i]].PB(i);
    }
    for(int i = n; i >= 2; i--)
    {
        if(xr[p[i]] && xr[p[i]] != xr[i] + 1) bl = true;
        xr[p[i]] = xr[i] + 1;
        for (int j = 0; j < 27; j++)
        {
            dp[p[i]][j] = max(dp[p[i]][j], dp[i][j] + a[i][j]);
            sum[i] += dp[i][j];
        }
        sum[i] -= dp[i][26];
    }
    if(bl)
    {
        for (int i = 0; i < q; i++)
        {
            cout << "Fou\n";
        }
        return 0;
    }
    for (int i = 0; i < 26; i++)
    {
        sum[1] += dp[1][i];
    }
    for (int i = 1; i <= n; i++)
    {
        if(l[i] != 1) cl[i] = true;
        col[i] = xr[i] < sum[i];
        qan += col[i];
    }
    cl[1] = true;
    for(int i = n; i >= 1; i--)
    {
        if(!cl[i])
            nerq[i] = nerq[g[i][0]];
        else
            nerq[i] = i;
        //cout << nerq[i] << " ";
    }
    dfs(1, 1);
    for (int i = 0; i < q; i++)
    {
        int v;
        char A;
        cin >> v >> A;
        int sm1, sm2;
        if(A == '?') sm1 = 26;
        else sm1 = A - 'a';
        sm2 = ln[v];
        ln[v] = sm1;
        v = nerq[v];
        a[v][sm1]++;
        a[v][sm2]--;
        upd(v, sm1, sm2);
        if(qan)
        {
            cout << "Fou\n";
        }
        else
        {
            int sm = 0;
            //cout << xr[1] << endl;
            for (int i = 0; i < 26; i++)
            {
                //if(dp[1][i]) cout << char('a' + i) << "-" << dp[1][i] << "\n";
                sm += (i + 1) * (dp[1][i] + xr[1] - sum[1]);
            }
            //cout << "?-" << dp[1][26] << endl;
            cout <<  "Shi " << sm << endl;
        }
    }
    return 0;
}


/*
	بسم الله الرحمن الرحیم

	(; طلا یک جهانی
*/

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 150 * 1000 + 5;
int n, q, par[MAX_N], up[MAX_N], cnt[MAX_N][26 + 5], h[MAX_N], tree_h, let[MAX_N];
vector<int> adj[MAX_N], vec;
bool good[MAX_N], all_good = true;

int convert(char c) {
	if (c == '?')
		return 26;
	return c - 'a';
}

void read_tree() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		char c;
		cin >> par[i] >> c;
		par[i]--;
		let[i] = convert(c);
		adj[par[i]].push_back(i);
	}
}

void dfs(int v, int cur_h = 0, int x = 0) {
	if (adj[v].empty()) {
		if (!tree_h || tree_h == cur_h)
			good[v] = true;
		else
			all_good = false;
		tree_h = cur_h;
		vec.push_back(v);
	}
	h[v] = cur_h;
	up[v] = x;
	for (int u: adj[v]) {
		if (adj[v].size() == 2 || !v)
			x = u;
		dfs(u, cur_h + 1, x);
	}
}

void update(int v) {
	int t = 0;
	for (int i = 0; i < 26; i++) {
		int cnt_old = cnt[v][i];
		cnt[v][i] = 0;
		for (int u: adj[v])
			cnt[v][i] = max(cnt[v][i], cnt[u][i] + (let[u] == i));
		t += cnt[v][i];
		if (up[v] != v)
			cnt[up[v]][i] += cnt[v][i] - cnt_old;
	}
	good[v] = t <= tree_h - h[v];
	for (int u: adj[v])
		good[v] &= good[u];
	good[up[v]] = good[v];
}

void solve() {
	dfs(0);
	for (int i = 0; i < (int) vec.size(); i++) {
		int v = vec[i];
		if (v)
			vec.push_back(par[v]);
	}
	for (int v: vec)
		update(v);
	for (int i = 0; i < n; i++)
		par[i] = par[up[i]];
}

int find_ans() {
	int t = 0;
	for (int i = 0; i < 26; i++)
		t += cnt[0][i];
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += (i + 1) * (tree_h - t + cnt[0][i]);
	return res;
}

void read_queries() {
	for (int i = 0; i < q; i++) {
		int v;
		char c;
		cin >> v >> c;
		v--;
		if (up[v] != v) {
			cnt[up[v]][let[v]]--;
			cnt[up[v]][convert(c)]++;
		}
		let[v] = convert(c);
		while (v && all_good) {
			update(par[v]);
			v = par[v];
		}
		if (i == -1) {
			for (int i = 0; i < 26; i++)
				cerr << char(i + 'a') << " : " << cnt[0][i] << endl;
		}
		if (good[0])
			cout << "Shi " << find_ans() << endl;
		else
			cout << "Fou" << endl;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	read_tree();
	solve();
	read_queries();
}


/*
	بسم الله الرحمن الرحیم

	(; طلا یک جهانی
*/

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 150 * 1000 + 5;
int n, q, par[MAX_N], cnt[MAX_N][26 + 5], h[MAX_N], tree_h, let[MAX_N];
vector<int> adj[MAX_N], vec;
bool good[MAX_N], all_good = true;

int convert(char c) {
	if (c == '?')
		return -1;
	return c - 'a';
}

void read_tree() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		char c;
		cin >> par[i] >> c;
		par[i]--;
		let[i] = convert(c);
		adj[par[i]].push_back(i);
	}
}

void dfs(int v, int cur_h = 0) {
	h[v] = cur_h;
	if (adj[v].empty()) {
		if (!tree_h || tree_h == cur_h)
			good[v] = true;
		else
			all_good = false;
		tree_h = cur_h;
		vec.push_back(v);
	}
	for (int u: adj[v])
		dfs(u, cur_h + 1);
}

void update(int v) {
	int t = 0;
	for (int i = 0; i < 26; i++) {
		cnt[v][i] = 0;
		for (int u: adj[v])
			cnt[v][i] = max(cnt[v][i], cnt[u][i] + (let[u] == i));
		t += cnt[v][i];
	}
	good[v] = t <= tree_h - h[v];
	for (int u: adj[v])
		good[v] &= good[u];
}

void solve() {
	dfs(0);
	for (int i = 0; i < (int) vec.size(); i++) {
		int v = vec[i];
		if (v)
			vec.push_back(par[v]);
	}
	for (int v: vec)
		update(v);
}

int find_ans() {
	int t = 0;
	for (int i = 0; i < 26; i++)
		t += cnt[0][i];
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += (i + 1) * (tree_h - t + cnt[0][i]);
	return res;
}

void read_queries() {
	for (int i = 0; i < q; i++) {
		int v;
		char c;
		cin >> v >> c;
		v--;
		let[v] = convert(c);
		while (v && all_good) {
			update(par[v]);
			v = par[v];
		}
		if (good[0])
			cout << "Shi " << find_ans() << endl;
		else
			cout << "Fou" << endl;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	read_tree();
	solve();
	read_queries();
}


/*
	بسم الله الرحمن الرحیم

	(; طلا یک جهانی
*/

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 150 * 1000 + 5;
int n, q, par[MAX_N], cnt[MAX_N][26 + 5], h[MAX_N], tree_h, let[MAX_N];
vector<int> adj[MAX_N], vec;
bool good[MAX_N];

int convert(char c) {
	if (c == '?')
		return -1;
	return c - 'a';
}

void read_tree() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		char c;
		cin >> par[i] >> c;
		par[i]--;
		let[i] = convert(c);
		adj[par[i]].push_back(i);
	}
}

void dfs(int v, int cur_h = 0) {
	h[v] = cur_h;
	if (adj[v].empty()) {
		if (!tree_h || tree_h == cur_h)
			good[v] = true;
		tree_h = cur_h;
		vec.push_back(v);
	}
	for (int u: adj[v])
		dfs(u, cur_h + 1);
}

void update(int v) {
	int t = 0;
	for (int i = 0; i < 26; i++) {
		cnt[v][i] = 0;
		for (int u: adj[v])
			cnt[v][i] = max(cnt[v][i], cnt[u][i] + (let[u] == i));
		t += cnt[v][i];
	}
	good[v] = t <= tree_h - h[v];
	for (int u: adj[v])
		good[v] &= good[u];
}

void solve() {
	dfs(0);
	for (int i = 0; i < (int) vec.size(); i++) {
		int v = vec[i];
		if (v)
			vec.push_back(par[v]);
	}
	for (int v: vec)
		update(v);
}

int find_ans() {
	int t = 0;
	for (int i = 0; i < 26; i++)
		t += cnt[0][i];
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += (i + 1) * (tree_h - t + cnt[0][i]);
	return res;
}

void read_queries() {
	for (int i = 0; i < q; i++) {
		int v;
		char c;
		cin >> v >> c;
		v--;
		let[v] = convert(c);
		while (v) {
			update(par[v]);
			v = par[v];
		}
		if (good[0])
			cout << "Shi " << find_ans() << endl;
		else
			cout << "Fou" << endl;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	read_tree();
	solve();
	read_queries();
}


/*
	بسم الله الرحمن الرحیم

	(; طلا یک جهانی
*/

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 150 * 1000 + 5;
int n, q, par[MAX_N], up[MAX_N], cnt[MAX_N][26 + 5], h[MAX_N], tree_h, let[MAX_N], new_let[MAX_N];
vector<int> adj[MAX_N], vec;
bool good[MAX_N], all_good = true;

int convert(char c) {
	if (c == '?')
		return 27;
	return c - 'a';
}

void read_tree() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		char c;
		cin >> par[i] >> c;
		par[i]--;
		let[i] = convert('?');
		new_let[i] = convert(c);
		adj[par[i]].push_back(i);
	}
}

void dfs(int v, int cur_h = 0, int x = 0) {
	if (adj[v].empty()) {
		if (!tree_h || tree_h == cur_h)
			good[v] = true;
		else
			all_good = false;
		tree_h = cur_h;
		vec.push_back(v);
	}
	h[v] = cur_h;
	up[v] = x;
	for (int u: adj[v]) {
		if (adj[v].size() == 2)
			x = u;
		dfs(u, cur_h + 1, x);
	}
}

void update(int v) {
	int w = up[v];
	if (w != v) {
		cnt[w][let[v]]--;
		cnt[w][new_let[v]]++;
	}
	let[v] = new_let[v];
	if (!w)
		return;
	v = par[w];
	int t = 0;
	for (int i = 0; i < 26; i++) {
		cnt[v][i] = 0;
		for (int u: adj[v])
			cnt[v][i] = max(cnt[v][i], cnt[u][i] + (let[u] == i));
		t += cnt[v][i];
	}
	good[v] = t <= tree_h - h[v];
	for (int u: adj[v])
		good[v] &= good[u];
}

void solve() {
	dfs(0);
	for (int i = 0; i < (int) vec.size(); i++) {
		int v = vec[i];
		if (par[v])
			vec.push_back(par[v]);
	}
	for (int v: vec) {
		good[up[v]] = good[v];
		update(v);
	}
}

int find_ans() {
	int t = 0;
	for (int i = 0; i < 26; i++)
		t += cnt[0][i];
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += (i + 1) * (tree_h - t + cnt[0][i]);
	return res;
}

void read_queries() {
	for (int i = 0; i < q; i++) {
		int v;
		char c;
		cin >> v >> c;
		v--;
		new_let[v] = convert(c);
		while (v && all_good) {
			update(v);
			v = par[up[v]];
			good[up[v]] = good[v];
		}
		if (good[0])
			cout << "Shi " << find_ans() << endl;
		else
			cout << "Fou" << endl;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	read_tree();
	solve();
	read_queries();
}


/*
	بسم الله الرحمن الرحیم

	(; طلا یک جهانی
*/

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 150 * 1000 + 5;
int n, q, par[MAX_N], up[MAX_N], cnt[MAX_N][26 + 5], h[MAX_N], tree_h, let[MAX_N];
vector<int> adj[MAX_N], vec;
bool good[MAX_N], all_good = true, mark[MAX_N];

int convert(char c) {
	if (c == '?')
		return 26;
	return c - 'a';
}

void read_tree() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		char c;
		cin >> par[i] >> c;
		par[i]--;
		let[i] = convert(c);
		adj[par[i]].push_back(i);
	}
}

void dfs(int v, int cur_h = 0, int x = 0) {
	if (adj[v].empty()) {
		if (!tree_h || tree_h == cur_h)
			good[v] = true;
		else
			all_good = false;
		tree_h = cur_h;
		vec.push_back(v);
	}
	h[v] = cur_h;
	up[v] = x;
	for (int u: adj[v]) {
		if (adj[v].size() == 2 || !v)
			x = u;
		dfs(u, cur_h + 1, x);
	}
}

void update(int v) {
	int t = 0;
	for (int i = 0; i < 26; i++) {
		int cnt_old = cnt[v][i];
		cnt[v][i] = 0;
		for (int u: adj[v])
			cnt[v][i] = max(cnt[v][i], cnt[u][i] + (let[u] == i));
		t += cnt[v][i];
		if (up[v] != v)
			cnt[up[v]][i] += cnt[v][i] - cnt_old;
	}
	good[v] = t <= tree_h - h[v];
	for (int u: adj[v])
		good[v] &= good[u];
	good[up[v]] = good[v];
}

void solve() {
	dfs(0);
	mark[0] = true;
	for (int i = 0; i < (int) vec.size(); i++) {
		int v = vec[i];
		if (!mark[par[v]]) {
			vec.push_back(par[v]);
			mark[par[v]] = true;
		}
	}
	for (int v: vec)
		update(v);
	for (int i = 0; i < n; i++)
		par[i] = par[up[i]];
}

int find_ans() {
	int t = 0;
	for (int i = 0; i < 26; i++)
		t += cnt[0][i];
	int res = 0;
	for (int i = 0; i < 26; i++)
		res += (i + 1) * (tree_h - t + cnt[0][i]);
	return res;
}

void read_queries() {
	for (int i = 0; i < q; i++) {
		int v;
		char c;
		cin >> v >> c;
		v--;
		if (up[v] != v) {
			cnt[up[v]][let[v]]--;
			cnt[up[v]][convert(c)]++;
		}
		let[v] = convert(c);
		while (v && all_good) {
			update(par[v]);
			v = par[v];
		}
		if (good[0])
			cout << "Shi " << find_ans() << endl;
		else
			cout << "Fou" << endl;
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	read_tree();
	solve();
	read_queries();
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <ctime>
#include <cassert>
#include <complex>
#include <string>
#include <cstring>
#include <chrono>
#include <random>
#include <queue>
#include <bitset>
using namespace std;

#ifdef LOCAL
	#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
	#define eprintf(...) 42
#endif

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int A = 26;
const int N = 150100;
int n, q;
int deg[N];
char s[N];
int a[N][2][A];
int par[N];
pii realPar[N];
bool imp[N];
vector<int> g[N];
int h[N];
bool ALL_BAD = false;
int cntBad = 0;
int H;
int curV[N];
int sumForV[N];

void dfs(int v, pii rp) {
	realPar[v] = rp;
	if (imp[v]) {
		for (int i = 0; i < (int)g[v].size(); i++)
			dfs(g[v][i], mp(v, i));
	} else {
		for (int u : g[v])
			dfs(u, rp);
	}
}

bool badV(int v) {
	return h[v] + sumForV[v] > H;
}

void setForLet(int v, int c, int p, int val) {
	if (badV(v)) cntBad--;
	sumForV[v] -= max(a[v][0][c], a[v][1][c]);
	a[v][p][c] = val;
	sumForV[v] += max(a[v][0][c], a[v][1][c]);
	if (badV(v)) cntBad++;
}

void updUp(int v, int c, int dx) {
	if (v == 0) return;
	bool st = false;
	while(v != 0) {
		int u = realPar[v].first;
		int p = realPar[v].second;
		if (!st) {
			setForLet(u, c, p, a[u][p][c] + dx);
		} else {
			setForLet(u, c, p, max(a[v][0][c], a[v][1][c]));
		}
		v = u;
		st = true;
	}
}

void printAns() {
	if (cntBad != 0) {
		printf("Fou\n");
		return;
	}
	int res = 0;
	for (int c = 0; c < A; c++) {
		res += (c + 1) * (H - sumForV[0] + max(a[0][0][c], a[0][1][c]));
	}
	printf("Shi %d\n", res);
}

int main()
{
//	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);

	scanf("%d%d", &n, &q);
	h[0] = 0;
	for (int i = 1; i < n; i++) {
		int v;
		char c;
		scanf("%d %c", &v, &c);
		v--;
		s[i] = c;
		par[i] = v;
		g[v].push_back(i);
		deg[v]++;
		h[i] = h[v] + 1;
	}
//	for (int i = 0; i < n; i++)
//		eprintf("%d ", h[i]);
//	eprintf("\n");
	H = -1;
	for (int i = 0; i < n; i++) {
		if (deg[i] != 0) continue;
		if (H == -1)
			H = h[i];
		if (H != h[i])
			ALL_BAD = true;
	}
	if (ALL_BAD) {
		while(q--) printf("Fou\n");
		return 0;
	}
//	eprintf("H = %d\n", H);
	for (int i = 0; i < n; i++)
		if (deg[i] == 2)
			imp[i] = 1;
	imp[0] = 1;
	dfs(0, mp(-1, -1));
	for (int i = 1; i < n; i++) {
		if (s[i] != '?')
			updUp(i, (int)(s[i] - 'a'), 1);
	}
	while(q--) {
		int v;
		char c;
		scanf("%d %c", &v, &c);
		v--;
		if (s[v] != '?')
			updUp(v, (int)(s[v] - 'a'), -1);
		s[v] = c;
		if (s[v] != '?')
			updUp(v, (int)(s[v] - 'a'), 1);
		printAns();
	}

	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <ctime>
#include <cassert>
#include <complex>
#include <string>
#include <cstring>
#include <chrono>
#include <random>
#include <queue>
#include <bitset>
using namespace std;

#ifdef LOCAL
	#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
	#define eprintf(...) 42
#endif

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int A = 26;
const int N = 150100;
int n, q;
int deg[N];
char s[N];
int a[N][2][A];
int par[N];
pii realPar[N];
bool imp[N];
vector<int> g[N];
int h[N];
bool ALL_BAD = false;
int cntBad = 0;
int H;
int curV[N];
int sumForV[N];

void dfs(int v, pii rp) {
	realPar[v] = rp;
	if (imp[v]) {
		for (int i = 0; i < (int)g[v].size(); i++)
			dfs(g[v][i], mp(v, i));
	} else {
		for (int u : g[v])
			dfs(u, rp);
	}
}

bool badV(int v) {
	return h[v] + sumForV[v] > H;
}

void setForLet(int v, int c, int p, int val) {
	if (badV(v)) cntBad--;
	sumForV[v] -= max(a[v][0][c], a[v][1][c]);
	a[v][p][c] = val;
	sumForV[v] += max(a[v][0][c], a[v][1][c]);
	if (badV(v)) cntBad++;
}

void updUp(int v, int c, int dx) {
	if (v == 0) return;
	bool st = false;
	while(v != 0) {
		int u = realPar[v].first;
		int p = realPar[v].second;
		int old = max(a[u][0][c], a[u][1][c]);
		setForLet(u, c, p, a[u][p][c] + dx);
		if (old == max(a[u][0][c], a[u][1][c])) break;
		v = u;
	}
}

void printAns() {
	if (cntBad != 0) {
		printf("Fou\n");
		return;
	}
	int res = 0;
	for (int c = 0; c < A; c++) {
		res += (c + 1) * (H - sumForV[0] + max(a[0][0][c], a[0][1][c]));
	}
	printf("Shi %d\n", res);
}

int main()
{
//	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);

	scanf("%d%d", &n, &q);
	h[0] = 0;
	for (int i = 1; i < n; i++) {
		int v;
		char c;
		scanf("%d %c", &v, &c);
		v--;
		s[i] = c;
		par[i] = v;
		g[v].push_back(i);
		deg[v]++;
		h[i] = h[v] + 1;
	}
//	for (int i = 0; i < n; i++)
//		eprintf("%d ", h[i]);
//	eprintf("\n");
	H = -1;
	for (int i = 0; i < n; i++) {
		if (deg[i] != 0) continue;
		if (H == -1)
			H = h[i];
		if (H != h[i])
			ALL_BAD = true;
	}
	if (ALL_BAD) {
		while(q--) printf("Fou\n");
		return 0;
	}
//	eprintf("H = %d\n", H);
	for (int i = 0; i < n; i++)
		if (deg[i] == 2)
			imp[i] = 1;
	imp[0] = 1;
	dfs(0, mp(-1, -1));
	for (int i = 1; i < n; i++) {
		if (s[i] != '?')
			updUp(i, (int)(s[i] - 'a'), 1);
	}
	while(q--) {
		int v;
		char c;
		scanf("%d %c", &v, &c);
		v--;
		if (s[v] != '?')
			updUp(v, (int)(s[v] - 'a'), -1);
		s[v] = c;
		if (s[v] != '?')
			updUp(v, (int)(s[v] - 'a'), 1);
		printAns();
	}

	return 0;
}


import java.io.OutputStream
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.util.Arrays
import java.util.StringTokenizer
import java.io.BufferedReader
import java.io.InputStreamReader

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
object Main {
    @JvmStatic
    fun main(args: Array<String>) {
        val inputStream = System.`in`
        val outputStream = System.out
        val `in` = InputReader(inputStream)
        val out = PrintWriter(outputStream)
        val solver = TaskD()
        solver.solve(1, `in`, out)
        out.close()
    }

    internal class TaskD {
        var WILDCARD = 26
        var numInvalid: Int = 0
        var leafDepth: Int = 0

        fun solve(testNumber: Int, `in`: InputReader, out: PrintWriter) {
            val n = `in`.nextInt()
            val q = `in`.nextInt()
            val vs = Array<TaskD.Vertex>(n, {i -> TaskD.Vertex()})
            vs[0].parentChar = -1
            for (i in 1 until n) {
                vs[i] = TaskD.Vertex()
                vs[i].parent = vs[`in`.nextInt() - 1]
                ++vs[i].parent!!.numChildren
                vs[i].depth = 1 + vs[i].parent!!.depth
                val ch = `in`.next()[0]
                if (ch == '?') {
                    vs[i].parentChar = WILDCARD
                } else {
                    vs[i].parentChar = ch - 'a'
                }
            }
            leafDepth = -1
            for (v in vs)
                if (v.numChildren == 0) {
                    if (leafDepth < 0) leafDepth = v.depth
                    if (leafDepth != v.depth) {
                        for (i in 0 until q) {
                            out.println("Fou")
                        }
                        return
                    }
                }
            if (leafDepth < 0) throw RuntimeException()
            var interesting = arrayOfNulls<TaskD.Vertex>(n)
            var interestingCount = 0
            for (v in vs) {
                if (v.parent == null || v.numChildren != 1) {
                    interesting[interestingCount++] = v
                }
            }
            interesting = Arrays.copyOf<Vertex>(interesting, interestingCount)
            reverseArray(interesting)
            for (v in interesting) {
                v!!.quickCount = IntArray(27)
                v.subtreeCount = IntArray(27)
            }
            numInvalid = 0
            for (v in interesting) {
                v!!.subtreeSum = 0
                for (i in 0..25) v.subtreeSum += v.subtreeCount!![i]
                if (v.subtreeSum > leafDepth - v.depth) {
                    ++numInvalid
                }
                if (v.parent != null) {
                    v.quickChild = v
                    ++v.quickCount!![v.parentChar]
                    var u = v.parent
                    while (u!!.parent != null && u.numChildren == 1) {
                        ++v.quickCount!![u.parentChar]
                        u.quickChild = v
                        u = u.parent
                    }
                    v.quickParent = u
                    for (i in 0..25) {
                        u.subtreeCount!![i] = Math.max(u.subtreeCount!![i], v.subtreeCount!![i] + v.quickCount!![i])
                    }
                    v.nextSibling = u.firstChild
                    u.firstChild = v
                }
            }
            for (qi in 0 until q) {
                val u = vs[`in`.nextInt() - 1]
                val v = u.quickChild
                val c1 = u.parentChar
                val ch = `in`.next()[0]
                val c2: Int
                if (ch == '?') {
                    c2 = WILDCARD
                } else {
                    c2 = ch - 'a'
                }
                if (c1 != c2) {
                    u.parentChar = c2
                    if (c1 < 26) {
                        --v!!.quickCount!![c1]
                        propagate(v, c1)
                    }
                    if (c2 < 26) {
                        ++v!!.quickCount!![c2]
                        propagate(v, c2)
                    }
                }
                if (numInvalid == 0) {
                    var s = 0
                    val spare = leafDepth - vs[0].subtreeSum
                    for (i in 0..25) {
                        s += (i + 1) * (vs[0].subtreeCount!![i] + spare)
                    }
                    out.println("Shi $s")
                } else {
                    out.println("Fou")
                }
            }
        }

        private fun propagate(v: TaskD.Vertex, what: Int) {
            val u = v.quickParent
            if (u!!.subtreeSum > leafDepth - u.depth) {
                --numInvalid
            }
            val old = u.subtreeCount!![what]
            u.subtreeSum -= old
            u.subtreeCount!![what] = 0
            var t = u.firstChild
            while (t != null) {
                u.subtreeCount!![what] = Math.max(u.subtreeCount!![what], t.subtreeCount!![what] + t.quickCount!![what])
                t = t.nextSibling
            }
            u.subtreeSum += u.subtreeCount!![what]
            if (u.subtreeSum > leafDepth - u.depth) {
                ++numInvalid
            }
            if (u.parent != null && u.subtreeCount!![what] != old) {
                propagate(u, what)
            }
        }

        private fun reverseArray(a: Array<TaskD.Vertex>) {
            var i = 0
            var j = a.size - 1
            while (i < j) {
                val t = a[i]
                a[i] = a[j]
                a[j] = t
                ++i
                --j
            }
        }

        internal class Vertex {
            var parent: TaskD.Vertex? = null
            var parentChar: Int = 0
            var depth: Int = 0
            var numChildren: Int = 0
            var quickParent: TaskD.Vertex? = null
            var quickChild: TaskD.Vertex? = null
            var quickCount: IntArray? = null
            var subtreeCount: IntArray? = null
            var firstChild: TaskD.Vertex? = null
            var nextSibling: TaskD.Vertex? = null
            var subtreeSum: Int = 0

        }

    }

    internal class InputReader(stream: InputStream) {
        var reader: BufferedReader
        var tokenizer: StringTokenizer? = null

        init {
            reader = BufferedReader(InputStreamReader(stream), 32768)
            tokenizer = null
        }

        operator fun next(): String {
            while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
                try {
                    tokenizer = StringTokenizer(reader.readLine())
                } catch (e: IOException) {
                    throw RuntimeException(e)
                }

            }
            return tokenizer!!.nextToken()
        }

        fun nextInt(): Int {
            return Integer.parseInt(next())
        }

    }
}


import java.io.OutputStream
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.util.Arrays
import java.util.StringTokenizer
import java.io.BufferedReader
import java.io.InputStreamReader

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
fun main() {
    val inputStream = System.`in`
    val outputStream = System.out
    val `in` = Main.InputReader(inputStream)
    val out = PrintWriter(outputStream)
    val solver = Main.TaskD()
    solver.solve(`in`, out)
    out.close()
}

object Main {
    internal class TaskD {
        var WILDCARD = 26
        var numInvalid: Int = 0
        var leafDepth: Int = 0

        fun solve(`in`: InputReader, out: PrintWriter) {
            val n = `in`.nextInt()
            val q = `in`.nextInt()
            val vs = Array<TaskD.Vertex>(n, {_ -> TaskD.Vertex()})
            vs[0].parentChar = -1
            for (i in 1 until n) {
                vs[i] = TaskD.Vertex()
                vs[i].parent = vs[`in`.nextInt() - 1]
                ++vs[i].parent!!.numChildren
                vs[i].depth = 1 + vs[i].parent!!.depth
                val ch = `in`.next()[0]
                if (ch == '?') {
                    vs[i].parentChar = WILDCARD
                } else {
                    vs[i].parentChar = ch - 'a'
                }
            }
            leafDepth = -1
            for (v in vs)
                if (v.numChildren == 0) {
                    if (leafDepth < 0) leafDepth = v.depth
                    if (leafDepth != v.depth) {
                        for (i in 0 until q) {
                            out.println("Fou")
                        }
                        return
                    }
                }
            if (leafDepth < 0) throw RuntimeException()
            var interesting = arrayOfNulls<TaskD.Vertex>(n)
            var interestingCount = 0
            for (v in vs) {
                if (v.parent == null || v.numChildren != 1) {
                    interesting[interestingCount++] = v
                }
            }
            interesting = Arrays.copyOf<Vertex>(interesting, interestingCount)
            reverseArray(interesting)
            for (v in interesting) {
                v!!.quickCount = IntArray(27)
                v.subtreeCount = IntArray(27)
            }
            numInvalid = 0
            for (v in interesting) {
                v!!.subtreeSum = 0
                for (i in 0..25) v.subtreeSum += v.subtreeCount!![i]
                if (v.subtreeSum > leafDepth - v.depth) {
                    ++numInvalid
                }
                if (v.parent != null) {
                    v.quickChild = v
                    ++v.quickCount!![v.parentChar]
                    var u = v.parent
                    while (u!!.parent != null && u.numChildren == 1) {
                        ++v.quickCount!![u.parentChar]
                        u.quickChild = v
                        u = u.parent
                    }
                    v.quickParent = u
                    for (i in 0..25) {
                        u.subtreeCount!![i] = Math.max(u.subtreeCount!![i], v.subtreeCount!![i] + v.quickCount!![i])
                    }
                    v.nextSibling = u.firstChild
                    u.firstChild = v
                }
            }
            for (qi in 0 until q) {
                val u = vs[`in`.nextInt() - 1]
                val v = u.quickChild
                val c1 = u.parentChar
                val ch = `in`.next()[0]
                val c2: Int
                if (ch == '?') {
                    c2 = WILDCARD
                } else {
                    c2 = ch - 'a'
                }
                if (c1 != c2) {
                    u.parentChar = c2
                    if (c1 < 26) {
                        --v!!.quickCount!![c1]
                        propagate(v, c1)
                    }
                    if (c2 < 26) {
                        ++v!!.quickCount!![c2]
                        propagate(v, c2)
                    }
                }
                if (numInvalid == 0) {
                    var s = 0
                    val spare = leafDepth - vs[0].subtreeSum
                    for (i in 0..25) {
                        s += (i + 1) * (vs[0].subtreeCount!![i] + spare)
                    }
                    out.println("Shi $s")
                } else {
                    out.println("Fou")
                }
            }
        }

        private fun propagate(v: TaskD.Vertex, what: Int) {
            val u = v.quickParent
            if (u!!.subtreeSum > leafDepth - u.depth) {
                --numInvalid
            }
            val old = u.subtreeCount!![what]
            u.subtreeSum -= old
            u.subtreeCount!![what] = 0
            var t = u.firstChild
            while (t != null) {
                u.subtreeCount!![what] = Math.max(u.subtreeCount!![what], t.subtreeCount!![what] + t.quickCount!![what])
                t = t.nextSibling
            }
            u.subtreeSum += u.subtreeCount!![what]
            if (u.subtreeSum > leafDepth - u.depth) {
                ++numInvalid
            }
            if (u.parent != null && u.subtreeCount!![what] != old) {
                propagate(u, what)
            }
        }

        private fun reverseArray(a: Array<TaskD.Vertex>) {
            var i = 0
            var j = a.size - 1
            while (i < j) {
                val t = a[i]
                a[i] = a[j]
                a[j] = t
                ++i
                --j
            }
        }

        internal class Vertex {
            var parent: TaskD.Vertex? = null
            var parentChar: Int = 0
            var depth: Int = 0
            var numChildren: Int = 0
            var quickParent: TaskD.Vertex? = null
            var quickChild: TaskD.Vertex? = null
            var quickCount: IntArray? = null
            var subtreeCount: IntArray? = null
            var firstChild: TaskD.Vertex? = null
            var nextSibling: TaskD.Vertex? = null
            var subtreeSum: Int = 0

        }

    }

    internal class InputReader(stream: InputStream) {
        var reader: BufferedReader
        var tokenizer: StringTokenizer? = null

        init {
            reader = BufferedReader(InputStreamReader(stream), 32768)
            tokenizer = null
        }

        operator fun next(): String {
            while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
                try {
                    tokenizer = StringTokenizer(reader.readLine())
                } catch (e: IOException) {
                    throw RuntimeException(e)
                }

            }
            return tokenizer!!.nextToken()
        }

        fun nextInt(): Int {
            return Integer.parseInt(next())
        }

    }
}


import java.io.OutputStream
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.util.Arrays
import java.util.StringTokenizer
import java.io.BufferedReader
import java.io.InputStreamReader

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
object Main {
    @JvmStatic
    fun main(args: Array<String>) {
        val inputStream = System.`in`
        val outputStream = System.out
        val `in` = InputReader(inputStream)
        val out = PrintWriter(outputStream)
        val solver = TaskD()
        solver.solve(`in`, out)
        out.close()
    }

    internal class TaskD {
        var WILDCARD = 26
        var numInvalid: Int = 0
        var leafDepth: Int = 0

        fun solve(`in`: InputReader, out: PrintWriter) {
            val n = `in`.nextInt()
            val q = `in`.nextInt()
            val vs = Array<TaskD.Vertex>(n, {_ -> TaskD.Vertex()})
            vs[0].parentChar = -1
            for (i in 1 until n) {
                vs[i] = TaskD.Vertex()
                vs[i].parent = vs[`in`.nextInt() - 1]
                ++vs[i].parent!!.numChildren
                vs[i].depth = 1 + vs[i].parent!!.depth
                val ch = `in`.next()[0]
                if (ch == '?') {
                    vs[i].parentChar = WILDCARD
                } else {
                    vs[i].parentChar = ch - 'a'
                }
            }
            leafDepth = -1
            for (v in vs)
                if (v.numChildren == 0) {
                    if (leafDepth < 0) leafDepth = v.depth
                    if (leafDepth != v.depth) {
                        for (i in 0 until q) {
                            out.println("Fou")
                        }
                        return
                    }
                }
            if (leafDepth < 0) throw RuntimeException()
            var interesting = arrayOfNulls<TaskD.Vertex>(n)
            var interestingCount = 0
            for (v in vs) {
                if (v.parent == null || v.numChildren != 1) {
                    interesting[interestingCount++] = v
                }
            }
            interesting = Arrays.copyOf<Vertex>(interesting, interestingCount)
            reverseArray(interesting)
            for (v in interesting) {
                v!!.quickCount = IntArray(27)
                v.subtreeCount = IntArray(27)
            }
            numInvalid = 0
            for (v in interesting) {
                v!!.subtreeSum = 0
                for (i in 0..25) v.subtreeSum += v.subtreeCount!![i]
                if (v.subtreeSum > leafDepth - v.depth) {
                    ++numInvalid
                }
                if (v.parent != null) {
                    v.quickChild = v
                    ++v.quickCount!![v.parentChar]
                    var u = v.parent
                    while (u!!.parent != null && u.numChildren == 1) {
                        ++v.quickCount!![u.parentChar]
                        u.quickChild = v
                        u = u.parent
                    }
                    v.quickParent = u
                    for (i in 0..25) {
                        u.subtreeCount!![i] = Math.max(u.subtreeCount!![i], v.subtreeCount!![i] + v.quickCount!![i])
                    }
                    v.nextSibling = u.firstChild
                    u.firstChild = v
                }
            }
            for (qi in 0 until q) {
                val u = vs[`in`.nextInt() - 1]
                val v = u.quickChild
                val c1 = u.parentChar
                val ch = `in`.next()[0]
                val c2: Int
                if (ch == '?') {
                    c2 = WILDCARD
                } else {
                    c2 = ch - 'a'
                }
                if (c1 != c2) {
                    u.parentChar = c2
                    if (c1 < 26) {
                        --v!!.quickCount!![c1]
                        propagate(v, c1)
                    }
                    if (c2 < 26) {
                        ++v!!.quickCount!![c2]
                        propagate(v, c2)
                    }
                }
                if (numInvalid == 0) {
                    var s = 0
                    val spare = leafDepth - vs[0].subtreeSum
                    for (i in 0..25) {
                        s += (i + 1) * (vs[0].subtreeCount!![i] + spare)
                    }
                    out.println("Shi $s")
                } else {
                    out.println("Fou")
                }
            }
        }

        private fun propagate(v: TaskD.Vertex, what: Int) {
            val u = v.quickParent
            if (u!!.subtreeSum > leafDepth - u.depth) {
                --numInvalid
            }
            val old = u.subtreeCount!![what]
            u.subtreeSum -= old
            u.subtreeCount!![what] = 0
            var t = u.firstChild
            while (t != null) {
                u.subtreeCount!![what] = Math.max(u.subtreeCount!![what], t.subtreeCount!![what] + t.quickCount!![what])
                t = t.nextSibling
            }
            u.subtreeSum += u.subtreeCount!![what]
            if (u.subtreeSum > leafDepth - u.depth) {
                ++numInvalid
            }
            if (u.parent != null && u.subtreeCount!![what] != old) {
                propagate(u, what)
            }
        }

        private fun reverseArray(a: Array<TaskD.Vertex>) {
            var i = 0
            var j = a.size - 1
            while (i < j) {
                val t = a[i]
                a[i] = a[j]
                a[j] = t
                ++i
                --j
            }
        }

        internal class Vertex {
            var parent: TaskD.Vertex? = null
            var parentChar: Int = 0
            var depth: Int = 0
            var numChildren: Int = 0
            var quickParent: TaskD.Vertex? = null
            var quickChild: TaskD.Vertex? = null
            var quickCount: IntArray? = null
            var subtreeCount: IntArray? = null
            var firstChild: TaskD.Vertex? = null
            var nextSibling: TaskD.Vertex? = null
            var subtreeSum: Int = 0

        }

    }

    internal class InputReader(stream: InputStream) {
        var reader: BufferedReader
        var tokenizer: StringTokenizer? = null

        init {
            reader = BufferedReader(InputStreamReader(stream), 32768)
            tokenizer = null
        }

        operator fun next(): String {
            while (tokenizer == null || !tokenizer!!.hasMoreTokens()) {
                try {
                    tokenizer = StringTokenizer(reader.readLine())
                } catch (e: IOException) {
                    throw RuntimeException(e)
                }

            }
            return tokenizer!!.nextToken()
        }

        fun nextInt(): Int {
            return Integer.parseInt(next())
        }

    }
}


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CF1168D {
    public static void main(String[] args) throws Exception {
        boolean local = System.getProperty("ONLINE_JUDGE") == null;
        boolean async = false;

        Charset charset = Charset.forName("ascii");

        FastIO io = local ? new FastIO(new FileInputStream("D:\\DATABASE\\TESTCASE\\Code.in"), System.out, charset) : new FastIO(System.in, System.out, charset);
        Task task = new Task(io, new Debug(local));

        if (async) {
            Thread t = new Thread(null, task, "dalt", 1 << 27);
            t.setPriority(Thread.MAX_PRIORITY);
            t.start();
            t.join();
        } else {
            task.run();
        }

        if (local) {
            io.cache.append("\n\n--memory -- \n" + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) >> 20) + "M");
        }

        io.flush();
    }

    public static class Task implements Runnable {
        final FastIO io;
        final Debug debug;
        int inf = (int) 1e8;

        public Task(FastIO io, Debug debug) {
            this.io = io;
            this.debug = debug;
        }

        @Override
        public void run() {
            solve();
        }

        Node[] nodes;

        public void solve() {
            int n = io.readInt();
            int q = io.readInt();
            nodes = new Node[n + 1];
            for (int i = 1; i <= n; i++) {
                nodes[i] = new Node();
                nodes[i].id = i;
            }
            int[] color = new int[n + 1];
            for (int i = 2; i <= n; i++) {
                setFather(nodes[i], nodes[io.readInt()]);
                color[i] = io.readChar();
                if (color[i] != '?') {
                    nodes[i].edges[color[i] - 'a']++;
                }
            }
            dfs(nodes[1], 1);
            compress(nodes[1], null);
            dpOnTree(nodes[1]);

            for (int i = 0; i < q; i++) {
                int nodeId = io.readInt();
                Node v = nodes[nodeId];
                int c = io.readChar();
                int old = color[nodeId];
                color[nodeId] = c;
                if (old != '?') {
                    v.edges[old - 'a']--;
                }
                if (c != '?') {
                    v.edges[c - 'a']++;
                }
                shiftUp(v);

                if (invalidCount > 0) {
                    io.cache.append("Fou\n");
                    continue;
                }

                int sum = 0;
                for (int j = 0; j < Node.SIZE; j++) {
                    sum += nodes[1].dp[j];
                }
                int flexible = leafDepth - 1 - sum;
                int func = 0;
                for (int j = 0; j < Node.SIZE; j++) {
                    func += (j + 1) * (flexible + nodes[1].dp[j]);
                }

                io.cache.append("Shi ").append(func).append('\n');
            }
        }

        public void dpOnNode(Node root, Node child) {
            for (int i = 0; i < Node.SIZE; i++) {
                root.dp[i] = Math.max(root.dp[i], root.edges[i] + child.dp[i]);
            }
        }

        public void shiftUp(Node root) {
            while (root != null) {
                dpOnNode(root);
                root = root.father;
            }
        }

        public void dpOnNode(Node root) {
            for (int i = 0; i < Node.SIZE; i++) {
                root.dp[i] = root.edges[i];
            }
            if (root.left != null) {
                dpOnNode(root, root.left);
            }
            if (root.right != null) {
                dpOnNode(root, root.right);
            }

            int sum = 0;
            for (int i = 0; i < Node.SIZE; i++) {
                sum += root.dp[i];
            }
            boolean valid = sum <= Math.min(leafDepth - root.depth + 1, leafDepth - 1);
            if (valid != root.valid) {
                if (valid) {
                    invalidCount--;
                } else {
                    invalidCount++;
                }
                root.valid = valid;
            }
        }

        public void dfs(Node root, int depth) {
            if (root == null) {
                return;
            }
            root.depth = depth;
            dfs(root.left, depth + 1);
            dfs(root.right, depth + 1);
            if (root.left == null && root.right == null) {
                if (leafDepth == -1) {
                    leafDepth = depth;
                }
                if (leafDepth != depth) {
                    invalidCount = inf;
                }
            }
        }

        int leafDepth = -1;
        int invalidCount = 0;

        public void dpOnTree(Node root) {
            if (root == null) {
                return;
            }
            dpOnTree(root.left);
            dpOnTree(root.right);
            dpOnNode(root);
        }

        public void compress(Node root, Node father) {
            if (root == null) {
                return;
            }
            root.father = father;
            while (root.left != null && root.right == null) {
                for (int i = 0; i < Node.SIZE; i++) {
                    root.edges[i] += root.left.edges[i];
                }
                nodes[root.left.id] = root;
                root.right = root.left.right;
                root.left = root.left.left;
            }
            compress(root.left, root);
            compress(root.right, root);
        }

        public void setFather(Node node, Node father) {
            node.father = father;
            if (father.left == null) {
                father.left = node;
            } else if (father.right == null) {
                father.right = node;
            }
        }
    }

    public static class Node {
        public static final int SIZE = 'z' - 'a' + 1;
        int[] dp = new int[SIZE];
        int[] edges = new int[SIZE];
        Node left;
        Node right;
        int depth;
        int id;
        Node father;
        boolean valid = true;
    }

    public static class FastIO {
        public final StringBuilder cache = new StringBuilder();
        private final InputStream is;
        private final OutputStream os;
        private final Charset charset;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 8);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastIO(InputStream is, OutputStream os, Charset charset) {
            this.is = is;
            this.os = os;
            this.charset = charset;
        }

        public FastIO(InputStream is, OutputStream os) {
            this(is, os, Charset.forName("ascii"));
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public long readLong() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            long val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public double readDouble() {
            boolean sign = true;
            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+';
                next = read();
            }

            long val = 0;
            while (next >= '0' && next <= '9') {
                val = val * 10 + next - '0';
                next = read();
            }
            if (next != '.') {
                return sign ? val : -val;
            }
            next = read();
            long radix = 1;
            long point = 0;
            while (next >= '0' && next <= '9') {
                point = point * 10 + next - '0';
                radix = radix * 10;
                next = read();
            }
            double result = val + (double) point / radix;
            return sign ? result : -result;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

        public int readLine(char[] data, int offset) {
            int originalOffset = offset;
            while (next != -1 && next != '\n') {
                data[offset++] = (char) next;
                next = read();
            }
            return offset - originalOffset;
        }

        public int readString(char[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (char) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public int readString(byte[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (byte) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public char readChar() {
            skipBlank();
            char c = (char) next;
            next = read();
            return c;
        }

        public void flush() {
            try {
                os.write(cache.toString().getBytes(charset));
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean hasMore() {
            skipBlank();
            return next != -1;
        }
    }

    public static class Debug {
        private boolean allowDebug;

        public Debug(boolean allowDebug) {
            this.allowDebug = allowDebug;
        }

        public void assertTrue(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (!flag) {
                fail();
            }
        }

        public void fail() {
            throw new RuntimeException();
        }

        public void assertFalse(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (flag) {
                fail();
            }
        }

        private void outputName(String name) {
            System.out.print(name + " = ");
        }

        public void debug(String name, int x) {
            if (!allowDebug) {
                return;
            }

            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, long x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, double x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, int[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, long[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, double[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, Object x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, Object... x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.deepToString(x));
        }
    }
}


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CF1168D {
    public static void main(String[] args) throws Exception {
        boolean local = System.getProperty("ONLINE_JUDGE") == null;
        boolean async = false;

        Charset charset = Charset.forName("ascii");

        FastIO io = local ? new FastIO(new FileInputStream("D:\\DATABASE\\TESTCASE\\Code.in"), System.out, charset) : new FastIO(System.in, System.out, charset);
        Task task = new Task(io, new Debug(local));

        if (async) {
            Thread t = new Thread(null, task, "dalt", 1 << 27);
            t.setPriority(Thread.MAX_PRIORITY);
            t.start();
            t.join();
        } else {
            task.run();
        }

        if (local) {
            io.cache.append("\n\n--memory -- \n" + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) >> 20) + "M");
        }

        io.flush();
    }

    public static class Task implements Runnable {
        final FastIO io;
        final Debug debug;
        int inf = (int) 1e8;

        public Task(FastIO io, Debug debug) {
            this.io = io;
            this.debug = debug;
        }

        @Override
        public void run() {
            solve();
        }

        Node[] nodes;

        public void solve() {
            int n = io.readInt();
            int q = io.readInt();
            nodes = new Node[n + 1];
            for (int i = 1; i <= n; i++) {
                nodes[i] = new Node();
                nodes[i].id = i;
            }
            int[] color = new int[n + 1];
            for (int i = 2; i <= n; i++) {
                setFather(nodes[i], nodes[io.readInt()]);
                color[i] = io.readChar();
                if (color[i] != '?') {
                    nodes[i].edges[color[i] - 'a']++;
                }
            }
            dfs(nodes[1], 1);
            compress(nodes[1], null);
            dpOnTree(nodes[1]);

            for (int i = 0; i < q; i++) {
                int nodeId = io.readInt();
                Node v = nodes[nodeId];
                int c = io.readChar();
                int old = color[nodeId];
                color[nodeId] = c;
                if (old != '?') {
                    v.edges[old - 'a']--;
                }
                if (c != '?') {
                    v.edges[c - 'a']++;
                }
                shiftUp(v);

                if (invalidCount > 0) {
                    io.cache.append("Fou\n");
                    continue;
                }

                int sum = 0;
                for (int j = 0; j < Node.SIZE; j++) {
                    sum += nodes[1].dp[j] + nodes[1].edges[j];
                }
                int flexible = leafDepth - 1 - sum;
                int func = 0;
                for (int j = 0; j < Node.SIZE; j++) {
                    func += (j + 1) * (flexible + nodes[1].dp[j] + nodes[1].edges[j]);
                }

                io.cache.append("Shi ").append(func).append('\n');
            }
        }

        public void dpOnNode(Node root, Node child) {
            for (int i = 0; i < Node.SIZE; i++) {
                root.dp[i] = Math.max(root.dp[i], child.edges[i] + child.dp[i]);
            }
        }

        public void shiftUp(Node root) {
            while (root != null) {
                dpOnNode(root);
                root = root.father;
            }
        }

        public void dpOnNode(Node root) {
            Arrays.fill(root.dp, 0);
            if (root.left != null) {
                dpOnNode(root, root.left);
                dpOnNode(root, root.right);
            } else {
                return;
            }
            int sum = 0;
            for (int i = 0; i < Node.SIZE; i++) {
                sum += root.dp[i];
            }
            boolean valid = sum <= Math.min(leafDepth - root.left.depth + 1, leafDepth - 1);
            if (valid != root.valid) {
                if (valid) {
                    invalidCount--;
                } else {
                    invalidCount++;
                }
                root.valid = valid;
            }
        }

        public void dfs(Node root, int depth) {
            if (root == null) {
                return;
            }
            root.depth = depth;
            dfs(root.left, depth + 1);
            dfs(root.right, depth + 1);
            if (root.left == null && root.right == null) {
                if (leafDepth == -1) {
                    leafDepth = depth;
                }
                if (leafDepth != depth) {
                    invalidCount = inf;
                }
            }
        }

        int leafDepth = -1;
        int invalidCount = 0;

        public void dpOnTree(Node root) {
            if (root == null) {
                return;
            }
            dpOnTree(root.left);
            dpOnTree(root.right);
            dpOnNode(root);
        }

        public void compress(Node root, Node father) {
            if (root == null) {
                return;
            }
            root.father = father;
            while (root.left != null && root.right == null) {
                for (int i = 0; i < Node.SIZE; i++) {
                    root.edges[i] += root.left.edges[i];
                }
                nodes[root.left.id] = root;
                root.right = root.left.right;
                root.left = root.left.left;
            }
            compress(root.left, root);
            compress(root.right, root);
        }

        public void setFather(Node node, Node father) {
            node.father = father;
            if (father.left == null) {
                father.left = node;
            } else if (father.right == null) {
                father.right = node;
            }
        }
    }

    public static class Node {
        public static final int SIZE = 'z' - 'a' + 1;
        int[] dp = new int[SIZE];
        int[] edges = new int[SIZE];
        Node left;
        Node right;
        int depth;
        int id;
        Node father;
        boolean valid = true;
    }

    public static class FastIO {
        public final StringBuilder cache = new StringBuilder();
        private final InputStream is;
        private final OutputStream os;
        private final Charset charset;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 8);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastIO(InputStream is, OutputStream os, Charset charset) {
            this.is = is;
            this.os = os;
            this.charset = charset;
        }

        public FastIO(InputStream is, OutputStream os) {
            this(is, os, Charset.forName("ascii"));
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public long readLong() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            long val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public double readDouble() {
            boolean sign = true;
            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+';
                next = read();
            }

            long val = 0;
            while (next >= '0' && next <= '9') {
                val = val * 10 + next - '0';
                next = read();
            }
            if (next != '.') {
                return sign ? val : -val;
            }
            next = read();
            long radix = 1;
            long point = 0;
            while (next >= '0' && next <= '9') {
                point = point * 10 + next - '0';
                radix = radix * 10;
                next = read();
            }
            double result = val + (double) point / radix;
            return sign ? result : -result;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

        public int readLine(char[] data, int offset) {
            int originalOffset = offset;
            while (next != -1 && next != '\n') {
                data[offset++] = (char) next;
                next = read();
            }
            return offset - originalOffset;
        }

        public int readString(char[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (char) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public int readString(byte[] data, int offset) {
            skipBlank();

            int originalOffset = offset;
            while (next > 32) {
                data[offset++] = (byte) next;
                next = read();
            }

            return offset - originalOffset;
        }

        public char readChar() {
            skipBlank();
            char c = (char) next;
            next = read();
            return c;
        }

        public void flush() {
            try {
                os.write(cache.toString().getBytes(charset));
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public boolean hasMore() {
            skipBlank();
            return next != -1;
        }
    }

    public static class Debug {
        private boolean allowDebug;

        public Debug(boolean allowDebug) {
            this.allowDebug = allowDebug;
        }

        public void assertTrue(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (!flag) {
                fail();
            }
        }

        public void fail() {
            throw new RuntimeException();
        }

        public void assertFalse(boolean flag) {
            if (!allowDebug) {
                return;
            }
            if (flag) {
                fail();
            }
        }

        private void outputName(String name) {
            System.out.print(name + " = ");
        }

        public void debug(String name, int x) {
            if (!allowDebug) {
                return;
            }

            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, long x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, double x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, int[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, long[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, double[] x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.toString(x));
        }

        public void debug(String name, Object x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println("" + x);
        }

        public void debug(String name, Object... x) {
            if (!allowDebug) {
                return;
            }
            outputName(name);
            System.out.println(Arrays.deepToString(x));
        }
    }
}