題解 洛谷 P5279 【[ZJOI2019]麻將】
阿新 • • 發佈:2020-11-13
這題非常的神啊。。。蒟蒻來寫一篇題解。
Solution
首先考慮如何判定一副牌是否是 "胡" 的。
不要想著統計個幾個值 \(O(1)\) 算,可以考慮複雜度大一點的。
首先先把 \(7\) 個對子的狀態判掉。然後考慮 \(4\) 個面子和 \(1\) 個對子的情況。
記錄一個 \(dp_{i, j,k}\) : \(i\) 表示現在有沒有留出對子,\(j\) 表示現在形如 \(i, i - 1\) 的牌的多餘的個數, \(k\) 表示現在形如 \(i\) 的牌對的個數,整個狀態表示現在的面子數量。
使用 dp 套 dp, 對於這個 \(dp_{i, j, k}\) 分別是多少給壓縮起來。這個狀態以及轉移可以 \(bfs\)
Code
#include<bits/stdc++.h> #define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++) #define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--) #define ll long long #define ull unsigned long long #define db double #define pii pair<int, int> #define mkp make_pair using namespace std; const int N = 405; const int M = 2200; const int mod = 998244353; int qpow(int x, int y) { if(x == 0) return 0; int res = 1; for(; y; x = 1ll * x * x % mod, y >>= 1) if(y & 1) res = 1ll * res * x % mod; return res; } int ny(int x) { return qpow(x, mod - 2); } int n, winid, ans; int max(int a, int b) { return a > b ? a : b; } void Max(int &a, int b) { a = max(a, b); } struct DPAM { int f[3][3]; void clear() { L(i, 0, 2) L(j, 0, 2) f[i][j] = -1; } }; DPAM operator + (DPAM aa, int bb) { DPAM res; res.clear(); L(i, 0, 2) L(j, 0, 2) if(aa.f[i][j] != -1) L(k, 0, 2) if(bb >= i + j + k) Max(res.f[j][k], min(aa.f[i][j] + i + (bb - i - j - k) / 3, 4)); return res; } void FMAX(DPAM &aa, DPAM bb) { L(i, 0, 2) L(j, 0, 2) Max(aa.f[i][j], bb.f[i][j]); } struct node { int cnt; DPAM dp[2]; void clear() { cnt = 0; dp[0].clear(), dp[1].clear(); } }; node win() { node res; res.clear(), res.cnt = 114514; return res; } bool check(node aa) { if(aa.cnt >= 7) return 1; L(i, 0, 2) L(j, 0, 2) if(aa.dp[1].f[i][j] >= 4) return 1; return 0; } bool operator < (node aa, node bb) { L(t, 0, 1) L(i, 0, 2) L(j, 0, 2) if(aa.dp[t].f[i][j] ^ bb.dp[t].f[i][j]) return aa.dp[t].f[i][j] < bb.dp[t].f[i][j]; return aa.cnt < bb.cnt; } node operator + (node aa, int bb) { if(aa.cnt == 114514) return aa; node res; res.clear(), res.cnt = aa.cnt + (bb >= 2); FMAX(res.dp[0], aa.dp[0] + bb); FMAX(res.dp[1], aa.dp[1] + bb); if(bb >= 2) FMAX(res.dp[1], aa.dp[0] + (bb - 2)); if(check(res)) return win(); return res; } int tot, dp[2][N][M]; map<node, int> mp; int G[M][5], jc[N], njc[N]; int C(int x, int y) { return 1ll * jc[x] * njc[x - y] % mod * njc[y] % mod; } void bfs() { queue<node> q; node gg; gg.clear(), gg.cnt = 0, gg.dp[0].f[0][0] = 0; mp[gg] = ++tot, q.push(gg); while(!q.empty()) { node u = q.front(); int id = mp[u]; q.pop(); L(i, 0, 4) { node v = u + i; if(!mp.count(v)) { G[id][i] = mp[v] = ++tot, q.push(v); if(v.cnt == 114514) winid = tot; } else G[id][i] = mp[v]; } } } int cnt[N]; void work() { dp[0][0][1] = 1; L(i, 1, n) { int now = (i & 1); memset(dp[now], 0, sizeof(dp[now])); L(j, 1, tot) L(k, 0, (i - 1) * 4) L(t, 0, 4 - cnt[i]) (dp[now][k + t][G[j][t + cnt[i]]] += 1ll * dp[now ^ 1][k][j] * C(4 - cnt[i], t) % mod) %= mod; } L(i, 0, n * 4 - 13) L(j, 1, tot) if(j != winid) (ans += 1ll * ny(C(4 * n - 13, i)) * dp[n & 1][i][j] % mod) %= mod; } int mian() { bfs(); scanf("%d", &n); jc[0] = njc[0] = 1; L(i, 1, n * 4) jc[i] = 1ll * jc[i - 1] * i % mod, njc[i] = ny(jc[i]); L(i, 1, 13) { int x, y; scanf("%d%d", &x, &y), cnt[x]++; } work(); printf("%d\n", ans); return 0; }