struct Status { // 自动机中的状态 int f[3][3][2], cnt; Status () { for (RI i = 0; i <= 2; ++i) for (RI j = 0; j <= 2; ++j) for (RI k = 0; k <= 1; ++k) f[i][j][k] = -0x3f3f3f3f; cnt = f[0][0][0] = 0; } void Trans(int res) { // 自动机状态的内部转移 Status nxt; int win = 0; nxt.cnt = cnt + (res >= 2); win |= (nxt.cnt >= 7); for (RI i = 0; i <= 2; ++i) for (RI j = 0; j <= 2; ++j) for (RI k = 0; k <= 1; ++k) { for (RI lasi = 0; lasi <= 2; ++lasi) for (RI lask = 0; lask <= k; ++lask) { int ned = lasi + i + j + 2 * (k - lask); if (ned > res) continue; nxt.f[i][j][k] = min(4, max(nxt.f[i][j][k], f[lasi][i][lask] + j + (res - ned >= 3))); } } for (RI i = 0; i <= 2; ++i) for (RI j = 0; j <= 2; ++j) for (RI k = 0; k <= 1; ++k) if (nxt.f[i][j][k] < 0) nxt.f[i][j][k] = -0x3f3f3f3f; win |= (nxt.f[0][0][1] >= 4); if (win) // 判胡牌 for (RI i = 0; i <= 2; ++i) for (RI j = 0; j <= 2; ++j) for (RI k = 0; k <= 1; ++k) nxt.f[i][j][k] = nxt.cnt = -1; *this = nxt; } bool operator < (const Status &A) const { if (cnt != A.cnt) return cnt < A.cnt; for (RI i = 0; i <= 2; ++i) for (RI j = 0; j <= 2; ++j) for (RI k = 0; k <= 1; ++k) if (f[i][j][k] != A.f[i][j][k]) return f[i][j][k] < A.f[i][j][k]; return 0; } }; map <Status, int> mp; queue <Status> q; int const MAXN = 2005, mod = 998244353; int child[MAXN][5]; int f[2][405][MAXN]; int tong[105]; int length; LL frac[MAXN], invfrac[MAXN];
LL qpow(LL a, LL k) { LL re = 1; for (; k; k >>= 1, a = a * a % mod) if (k & 1) re = re * a % mod; return re; } void Init(int Max) { frac[0] = 1; for (RI i = 1; i <= Max; ++i) frac[i] = frac[i - 1] * i % mod; invfrac[Max] = qpow(frac[Max], mod - 2); for (RI i = Max; i >= 1; --i) invfrac[i - 1] = invfrac[i] * i % mod; }
void GetStatus() { // 上文已经解释了 Status t, nxt; int cnt = 0; q.push(t), mp[t] = ++cnt; while (t.cnt != -1) t.Trans(2); mp[t] = ++cnt; for (RI i = 0; i <= 4; ++i) child[mp[t]][i] = mp[t]; while (!q.empty()) { t = q.front(); q.pop(); for (RI i = 0; i <= 4; ++i) { nxt = t, nxt.Trans(i); if (mp.find(nxt) == mp.end()) q.push(nxt), mp[nxt] = ++cnt; child[mp[t]][i] = mp[nxt]; } if (cnt > 100000) break; // protection } length = mp.size(); }
inline void Add(int &x, int y) { x += y - mod; x += (x >> 31) & mod; }
LL C[10][10];
int main() { #ifdef LOCAL FILEIO("a"); #endif
GetStatus(); int n; cin >> n; C[0][0] = 1; C[1][0] = C[1][1] = 1; C[2][0] = C[2][2] = 1, C[2][1] = 2; C[3][0] = C[3][3] = 1, C[3][1] = C[3][2] = 3; C[4][0] = C[4][4] = 1, C[4][1] = C[4][3] = 4, C[4][2] = 6; Init(4 * n); for (RI i = 1, x, y; i <= 13; ++i) cin >> x >> y, ++tong[x]; int cur = 0, nxt = 1; f[nxt][0][1] = 1; for (RI i = 1; i <= n; ++i) { // 简单 DP swap(cur, nxt); memset(f[nxt], 0, sizeof(f[nxt])); for (RI j = 0; j <= 4 * (i - 1); ++j) for (RI now = 1; now <= length; ++now) for (RI k = tong[i]; k <= 4; ++k) Add(f[nxt][j + k][child[now][k]], 1ll * f[cur][j][now] * C[4 - tong[i]][k - tong[i]] % mod); } int ans = 0; for (RI i = 14; i <= 4 * n; ++i) for (RI j = 1; j <= length; ++j) if (j != 2) Add(ans, 1ll * f[nxt][i][j] * frac[4 * n - i] % mod * frac[i - 13] % mod); ans = (1ll * ans * invfrac[4 * n - 13] % mod + 1) % mod; cout << ans << endl;
return 0; }
// created by Daniel yuan /* ________ / \ / / \ \ / / \ \ \ / \ ______ / \________/ */