#include using namespace std; using ll = long long; static constexpr ll MOD = 998244353; ll mod_pow(ll a, ll e) { ll r = 1; while (e) { if (e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; string S; cin >> S; // ----------------------------- // factorial / inverse factorial // ----------------------------- vector fact(N + 1), ifact(N + 1); fact[0] = 1; for (int i = 1; i <= N; ++i) { fact[i] = fact[i - 1] * i % MOD; } ifact[N] = mod_pow(fact[N], MOD - 2); for (int i = N; i >= 1; --i) { ifact[i - 1] = ifact[i] * i % MOD; } auto C = [&](int n, int k) -> ll { if (k < 0 || k > n) return 0; return fact[n] * ifact[k] % MOD * ifact[n - k] % MOD; }; // ----------------------------- // Catalan // cat[m] = Cat(m) // icat[m] = 1 / Cat(m) // ----------------------------- vector cat(N / 2 + 1); vector icat(N / 2 + 1); for (int m = 0; m <= N / 2; ++m) { // Cat(m) = C(2m,m) - C(2m,m-1) cat[m] = (C(2 * m, m) - C(2 * m, m - 1) + MOD) % MOD; // Cat(m)^(-1) // Cat(m) = (2m)! / (m! m! (m+1)) icat[m] = ifact[2 * m] * fact[m] % MOD * fact[m] % MOD * (m + 1) % MOD; } // completed runs の情報 int bad = 0; ll prod = 1; auto add_gap = [&](int len) { if (len & 1) { ++bad; } else { prod = prod * cat[len / 2] % MOD; } }; auto erase_gap = [&](int len) { if (len & 1) { --bad; } else { prod = prod * icat[len / 2] % MOD; } }; /* cuts: S[x-1] != S[x] となる x を格納する。 x は 人 x | 人 x+1 の境界。 0 は sentinel。 例: S = YYNNYYY ^ x=2 */ set cuts; cuts.insert(0); int last = 0; for (int x = 1; x < N; ++x) { if (S[x - 1] != S[x]) { // last -> x は completed run add_gap(x - last); // x は昇順なので hint を使う cuts.insert(cuts.end(), x); last = x; } } /* 境界 x の有無を反転する。 */ auto toggle_cut = [&](int x) { if (x <= 0 || x >= N) return; auto it = cuts.find(x); // ----------------------------- // x を追加 // ----------------------------- if (it == cuts.end()) { auto rit = cuts.lower_bound(x); int l = *prev(rit); if (rit != cuts.end()) { int r = *rit; /* before: l -------- r after: l ---- x ---- r r-l を消して x-l, r-x を追加 */ erase_gap(r - l); add_gap(x - l); add_gap(r - x); } else { /* x が新しい最後の境界。 before: l -------- 最後のrun after: l ---- x ---- 最後のrun x-l が completed run になる。 */ add_gap(x - l); } cuts.insert(rit, x); } // ----------------------------- // x を削除 // ----------------------------- else { auto rit = next(it); int l = *prev(it); if (rit != cuts.end()) { int r = *rit; /* before: l ---- x ---- r after: l -------- r */ erase_gap(x - l); erase_gap(r - x); add_gap(r - l); } else { /* x が最後の境界だった。 x-l が completed ではなくなる。 */ erase_gap(x - l); } cuts.erase(it); } }; while (Q--) { int type, x; cin >> type >> x; if (type == 1) { int i = x; // 1-indexed /* 人 i の文字を反転すると (i-1, i) (i, i+1) の境界だけが反転する。 境界番号では i-1, i。 */ toggle_cut(i - 1); toggle_cut(i); S[i - 1] = (S[i - 1] == 'Y' ? 'N' : 'Y'); } else { int K = x; // completed run に奇数長が存在 if (bad > 0) { cout << 0 << '\n'; continue; } /* 最後の境界。 p=0 なら S 全体が一つの run。 */ int p = *cuts.rbegin(); int L = N - p; // completed 部分では嘘つき数は p/2 に固定 ll r = (ll)K - p / 2; ll ways = 0; if (0 <= r && r <= L) { // 最後の run が Y if (S[p] == 'Y') { if (r <= L / 2) { ways = (C(L, (int)r) - C(L, (int)r - 1) + MOD) % MOD; } } // 最後の run が N else { if (r >= (L + 1) / 2) { ways = (C(L, (int)r) - C(L, (int)r + 1) + MOD) % MOD; } } } cout << prod * ways % MOD << '\n'; } } return 0; }