AtCoder Grand Contest 046 简要题解
从这里开始
达成成就:不看 agc 题解补完一场 agc。
感觉是我做过的最无聊的一场 agc,没有之一。让我来回顾一下我做了什么:
- 大力猜结论
- 大力猜结论
- 好难啊,哦,没看到 respectively
- 大力猜结论
- 大力猜结论
- #include "1338E",大力猜结论
为什么这场出题人不学习一下隔壁 global round 9。
Problem A Takahashikun, The Strider
显然答案是 $\frac{360}{(X, 360)}$。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; template <typename T> boolean vmin(T& a, T b) { return (a > b) ? (a = b, true) : (false); } template <typename T> boolean vmax(T& a, T b) { return (a < b) ? (a = b, true) : (false); } template <typename T> T smax(T x) { return x; } template <typename T, typename ...K> T smax(T a, const K &...args) { return max(a, smax(args...)); } template <typename T> T smin(T x) { return x; } template <typename T, typename ...K> T smin(T a, const K &...args) { return min(a, smin(args...)); } // debugging lib #define VN(x) #x #define Vmsg(x) VN(x) << " = " << (x) #define printv(x) cerr << VN(x) << " = " << (x); #define debug(...) fprintf(stderr, __VA_ARGS__); template <typename A, typename B> ostream& operator << (ostream& os, const pair<A, B>& z) { os << "(" << z.first << ", " << z.second << ')'; return os; } template <typename T> ostream& operator << (ostream& os, const vector<T>& a) { boolean isfirst = true; os << "{"; for (auto z : a) { if (!isfirst) { os << ", "; } os << z; isfirst = false; } os << '}'; return os; } #define pii pair<int, int> #define pil pair<int, ll> #define pli pair<ll, int> #define ll long long #define ull unsigned long long const int inf = (signed) (~0u >> 2); const ll llf = (signed ll) (~0ull >> 2); #define pb push_back #define eb emplace_back #define fi first #define sc second template <typename T> int vsize(vector<T>& x) { return (signed) x.size(); } template <typename T> int discrete(T* a, int* b, int n) { vector<T> v(a + 1, a + n + 1); sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1; return v.size(); } mt19937 rng (time(NULL)); int randint(int l, int r) { return rng() % (r - l + 1) + l; } int X; int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> X; cout << 360 / __gcd(X, 360) << '\n'; return 0; }
Problem B Extension
考虑怎么判断一个局面是否可行。你相当于是要找一条折线只能向右和向上的折线,使得划开的两部分,一部分每列恰好有 1 个黑格子,一个每行恰好有 1 个黑格子。
考虑对于必须要分给行的一个黑格子,那一列一定存在至少 2 个白格子。对于一列有至少 2 个白格子相当于是要求这个位置的折线高度至多为多少。
从右往左考虑每列,记录最高的折线高度 dp。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; template <typename T> boolean vmin(T& a, T b) { return (a > b) ? (a = b, true) : (false); } template <typename T> boolean vmax(T& a, T b) { return (a < b) ? (a = b, true) : (false); } template <typename T> T smax(T x) { return x; } template <typename T, typename ...K> T smax(T a, const K &...args) { return max(a, smax(args...)); } template <typename T> T smin(T x) { return x; } template <typename T, typename ...K> T smin(T a, const K &...args) { return min(a, smin(args...)); } // debugging lib #define VN(x) #x #define Vmsg(x) VN(x) << " = " << (x) #define printv(x) cerr << VN(x) << " = " << (x); #define debug(...) fprintf(stderr, __VA_ARGS__); template <typename A, typename B> ostream& operator << (ostream& os, const pair<A, B>& z) { os << "(" << z.first << ", " << z.second << ')'; return os; } template <typename T> ostream& operator << (ostream& os, const vector<T>& a) { boolean isfirst = true; os << "{"; for (auto z : a) { if (!isfirst) { os << ", "; } os << z; isfirst = false; } os << '}'; return os; } #define pii pair<int, int> #define pil pair<int, ll> #define pli pair<ll, int> #define ll long long #define ull unsigned long long const int inf = (signed) (~0u >> 2); const ll llf = (signed ll) (~0ull >> 2); #define pb push_back #define eb emplace_back #define fi first #define sc second template <typename T> int vsize(vector<T>& x) { return (signed) x.size(); } template <typename T> int discrete(T* a, int* b, int n) { vector<T> v(a + 1, a + n + 1); sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1; return v.size(); } mt19937 rng (time(NULL)); int randint(int l, int r) { return rng() % (r - l + 1) + l; } #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend boolean operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; const int N = 3005; int A, B, C, D; Zi f[N][N]; int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> A >> B >> C >> D; int da = C - A, db = D - B; f[da + 1][db] = 1; for (int i = da; i; i--) { for (int j = 0; j <= db; j++) { f[i][j] += f[i + 1][j] * (j + B); } Zi sum = 0; for (int j = db; j--; ) { sum = sum * (i + A) + f[i + 1][j + 1]; f[i][j] += sum * (j + B); } } Zi ans = 0; for (int i = db; ~i; i--) { ans = ans * A + f[1][i]; } cout << ans.v << '\n'; return 0; }
Problem C Shift
考虑 0 分割开的若干连续段,每次操作相当于把后面某个连续段长度 - 1,前面某个连续段长度 + 1。dp 即可。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; template <typename T> boolean vmin(T& a, T b) { return (a > b) ? (a = b, true) : (false); } template <typename T> boolean vmax(T& a, T b) { return (a < b) ? (a = b, true) : (false); } template <typename T> T smax(T x) { return x; } template <typename T, typename ...K> T smax(T a, const K &...args) { return max(a, smax(args...)); } template <typename T> T smin(T x) { return x; } template <typename T, typename ...K> T smin(T a, const K &...args) { return min(a, smin(args...)); } // debugging lib #define VN(x) #x #define Vmsg(x) VN(x) << " = " << (x) #define printv(x) cerr << VN(x) << " = " << (x); #define debug(...) fprintf(stderr, __VA_ARGS__); template <typename A, typename B> ostream& operator << (ostream& os, const pair<A, B>& z) { os << "(" << z.first << ", " << z.second << ')'; return os; } template <typename T> ostream& operator << (ostream& os, const vector<T>& a) { boolean isfirst = true; os << "{"; for (auto z : a) { if (!isfirst) { os << ", "; } os << z; isfirst = false; } os << '}'; return os; } #define pii pair<int, int> #define pil pair<int, ll> #define pli pair<ll, int> #define ll long long #define ull unsigned long long const int inf = (signed) (~0u >> 2); const ll llf = (signed ll) (~0ull >> 2); #define pb push_back #define eb emplace_back #define fi first #define sc second template <typename T> int vsize(vector<T>& x) { return (signed) x.size(); } template <typename T> int discrete(T* a, int* b, int n) { vector<T> v(a + 1, a + n + 1); sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1; return v.size(); } mt19937 rng (time(NULL)); int randint(int l, int r) { return rng() % (r - l + 1) + l; } #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend boolean operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; const int N = 305; int K; char s[N]; Zi f[N][N], g[N][N]; int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); scanf("%s%d", s + 1, &K); int n = strlen(s + 1); K = min(K, n); vector<int> pos0; for (int i = 1; i <= n; i++) { if (s[i] == '0') { pos0.push_back(i); } } pos0.insert(pos0.begin(), 0); pos0.push_back(n + 1); vector<int> len; for (int i = 1; i < (signed) pos0.size(); i++) { len.push_back(pos0[i] - pos0[i - 1] - 1); } reverse(len.begin(), len.end()); f[0][0] = 1; int suml = 0; for (auto l : len) { suml += l; for (int i = 0; i <= suml; i++) { memset(g[i], 0, (suml + 1) << 2); } for (int i = 0; i <= suml; i++) { for (int j = 0; j <= suml; j++) { g[i][j] = f[i][j]; if (i && j) g[i][j] += g[i - 1][j - 1]; } } for (int i = suml; i > l; i--) { for (int j = suml; j > l; j--) { g[i][j] -= g[i - l - 1][j - l - 1]; } } for (int i = 0; i <= suml; i++) { Zi sum = 0; for (int j = suml; ~j; j--) { sum += f[i][j + 1]; g[i][j] += sum; } } for (int i = 0; i <= suml; i++) { for (int j = 0; j <= suml; j++) { f[i][j] = g[i][j]; } } } Zi ans = 0; for (int i = 0; i <= K; i++) { ans += f[i][0]; } printf("%d\n", ans.v); return 0; }
Problem D Secret Passage
显然主要问题是给定一个串 $s$ 判断是否可达。考虑把操作序列倒过来,相当于每次删除一个字符在前面这一个,然后再加入一个任意字符(这两个顺序可以颠倒),判定能否到达初始的串 $S$。
感性理解一下,只用记录最长的等于 $S$ 的某个后缀的子序列长度,以及还有多少个 $0$,以及 $1$ 不在这里面。
证明在路上了。
显然填串 $s$ 的过程,和给定上述状态判断是否可行可以分开 dp。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; template <typename T> boolean vmin(T& a, T b) { return (a > b) ? (a = b, true) : (false); } template <typename T> boolean vmax(T& a, T b) { return (a < b) ? (a = b, true) : (false); } template <typename T> T smax(T x) { return x; } template <typename T, typename ...K> T smax(T a, const K &...args) { return max(a, smax(args...)); } template <typename T> T smin(T x) { return x; } template <typename T, typename ...K> T smin(T a, const K &...args) { return min(a, smin(args...)); } // debugging lib #define VN(x) #x #define Vmsg(x) VN(x) << " = " << (x) #define printv(x) cerr << VN(x) << " = " << (x); #define debug(...) fprintf(stderr, __VA_ARGS__); template <typename A, typename B> ostream& operator << (ostream& os, const pair<A, B>& z) { os << "(" << z.first << ", " << z.second << ')'; return os; } template <typename T> ostream& operator << (ostream& os, const vector<T>& a) { boolean isfirst = true; os << "{"; for (auto z : a) { if (!isfirst) { os << ", "; } os << z; isfirst = false; } os << '}'; return os; } #define pii pair<int, int> #define pil pair<int, ll> #define pli pair<ll, int> #define ll long long #define ull unsigned long long const int inf = (signed) (~0u >> 2); const ll llf = (signed ll) (~0ull >> 2); #define pb push_back #define eb emplace_back #define fi first #define sc second template <typename T> int vsize(vector<T>& x) { return (signed) x.size(); } template <typename T> int discrete(T* a, int* b, int n) { vector<T> v(a + 1, a + n + 1); sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1; return v.size(); } mt19937 rng (time(NULL)); int randint(int l, int r) { return rng() % (r - l + 1) + l; } #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend boolean operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; const int N = 305; int n; char s[N]; bool f[N][N][N]; Zi g[N][N][N]; set<string> S; void check(string t) { int len = t.length(); int p = n, x = 0, y = 0; for (int i = len; i--; ) { if (t[i] == s[p]) { p--; } else { (t[i] == '0' ? x : y)++; } } if (!f[p][x][y]) { cerr << t << '\n'; } } void dfs(string s) { if (S.count(s)) return; S.insert(s); check(s); if (s.length() < 2u) return; string t = s; t.erase(t.begin()); t.erase(t.begin()); char x = s[0], y = s[1]; for (int i = 0; i < (signed) t.size(); i++) { string nt = t; nt.insert(nt.begin() + i, x); dfs(nt); nt = t; nt.insert(nt.begin() + i, y); dfs(nt); } string nt = t; nt += x; dfs(nt); nt = t; nt += y; dfs(nt); } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> (s + 1); n = strlen(s + 1); f[0][0][0] = true; for (int i = 1; i <= n; i++) { for (int j = n; ~j; j--) { for (int k = n; ~k; k--) { if (i == n && !j && !k) continue; f[i][j][k] |= f[i - 1][j][k]; if (s[i] == '0') f[i][j][k] |= f[i][j][k + 1]; if (s[i] == '1') f[i][j][k] |= f[i][j + 1][k]; } } for (int j = 0; j <= n; j++) { for (int k = 0; k <= n; k++) { if (i > 1) { if ((s[i] == '0' || s[i - 1] == '0') && j) { f[i][j][k] |= f[i - 2][j - 1][k]; } if ((s[i] == '0' && s[i - 1] == '0') && j) { f[i][j][k] |= f[i - 1][j - 1][k + 1]; } if ((s[i] == '1' || s[i - 1] == '1') && k) { f[i][j][k] |= f[i - 2][j][k - 1]; } if ((s[i] == '1' && s[i - 1] == '1') && k) { f[i][j][k] |= f[i - 1][j + 1][k - 1]; } } } } } g[n + 1][0][0] = 1; for (int i = n + 1; i > 1; i--) { for (int j = 0; j <= n; j++) { for (int k = 0; k <= n; k++) { Zi v = g[i][j][k]; if (!v.v) continue; g[i - 1][j][k] += v; if (s[i - 1] == '0') g[i][j][k + 1] += v; if (s[i - 1] == '1') g[i][j + 1][k] += v; } } } Zi ans = 0; for (int i = 0; i <= n; i++) { for (int j = 0; j <= i; j++) { for (int k = 0; k <= i; k++) { if (f[i][j][k]) { ans += g[i + 1][j][k]; // cerr << g[i + 1][j][k].v << " " << i + 1 << " " << j << " "<< k << '\n'; } } } } // dfs(string(s + 1)); printf("%d\n", ans.v); return 0; }
Problem E Permutation Cover
显然充要条件是 $\max a_i \leqslant 2\min a_i$。
考虑枚举每一位,枚举它的值,判断是否有一个合法序列的前缀为它。
感性理解一下:只用判断补全一个包含它的排列,在再后面添加不超过 $K - 1$ 个数后,要求添加这些数现在都被某个排列覆盖,剩下的所有数能否构成合法序列。
证明在路上了。
Code
#include <bits/stdc++.h> using namespace std; const int N = 105; int n, m; vector<int> c, p; vector<bool> cov; vector<int> vmi, vpmi; bool chk0() { int mx = *max_element(c.begin(), c.end()); int mi = *min_element(c.begin(), c.end()); return mx <= 2 * mi; } int main() { scanf("%d", &m); c.resize(m); for (auto& x : c) { scanf("%d", &x); n += x; } if (!chk0()) { puts("-1"); return 0; } p.resize(n); cov.resize(n, false); vmi.resize(n + 1, 0); vpmi.resize(n + 1, 0); for (int i = 0; i < n; i++) { for (int x = 0; x < m; x++) { if (!c[x]) continue; auto bc = c; auto bcov = cov; bc[x]--; p[i] = x; if (i >= m - 1) { vector<bool> vis(m, false); for (int j = 0; j < m; j++) { vis[p[i - j]] = true; } if (*min_element(vis.begin(), vis.end())) { fill(bcov.begin() + (i - m + 1), bcov.begin() + i + 1, true); } } int pr = i - 1; while (~pr && p[pr] != x) pr--; if (pr >= 0 && !*min_element(bcov.begin(), bcov.begin() + pr + 1)) { continue; } int R = i + 1; while (R && !bcov[R - 1]) R--; bool flg = false; vector<bool> vis(m, false); int L = i + 1; while (L && !vis[p[L - 1]]) vis[p[--L]] = true; int mi = 114514, mx = 0; for (int j = 0; j < m; j++) { mi = min(bc[j] - !vis[j], mi); mx = max(bc[j] - 1, mx); } int mip = i + 1; for (int j = L; j <= i; j++) { int y = p[j]; if (bc[y] == mi) { mip = min(mip, j); } } vmi[L] = mi; vpmi[L] = mip; for (int j = L; j <= i; j++) { int y = p[j]; if (bc[y] - 1 < mi) { vmi[j + 1] = bc[y] - 1; vpmi[j + 1] = i + 1; } else { vmi[j + 1] = vmi[j]; vpmi[j + 1] = vpmi[j]; } } int mxp = i + 1; for (int j = i; j >= L; j--) { int y = p[j]; if (bc[y] > mx) { mxp = j; mx = bc[y]; } if (j <= R) { if (vmi[j] >= 0 && mx <= 2 * vmi[j] || (mx == 2 * vmi[j] + 1 && mxp <= vpmi[j])) { flg = true; break; } } } if (L > i && *max_element(bc.begin(), bc.end()) <= 2 * *min_element(bc.begin(), bc.end())) { flg = true; } if (flg) { cov = bcov; c = bc; break; } } } assert(!*max_element(c.begin(), c.end())); assert(*min_element(cov.begin(), cov.end())); for (auto x : p) { printf("%d ", x + 1); } return 0; }
Problem F Forbidden Tournament
请先 get 一下前置结论
先除去入度为 0 的点。设剩下的点中标号最小的为 $X$。
因为 $in(X)$ 不存在环,所以设 $P_i$ 是所有有到 $X$ 的边的点,并且 $P_i$ 有边到 $P_{i - 1}$。
因为 $out(X)$ 也不存在环,类似地定义 $Q_i$。
设 $L = |in(X)|$
不难发现和证明以下结论:
- 存在边 $Q_1 \rightarrow P_L$。
- 如果存在边 $Q_i \rightarrow P_j (i > 1)$,那么存在边 $Q_{i-1} \rightarrow P_j$
- 如果存在边 $Q_i \rightarrow P_j (j < L)$,那么存在边 $Q_i \rightarrow P_{j + 1}$
- 上述条件是充分的
然后枚举一下 $|in(X)|, |out(X)|$,做个简单 dp 就行了。
Code
#include <bits/stdc++.h> using namespace std; #ifdef local #define _assert(expr) assert(expr) #else #define _assert(expr) #endif int Mod = 998244353; typedef long long ll; void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int Mod = ::Mod) { int x, y; exgcd(a, Mod, x, y); return (x < 0) ? (x + Mod) : x; } class Z { public: int v; Z() { } Z(int v) : v(v) { _assert(v >= 0 && v < Mod); } Z(ll x) : v(x % Mod) { } friend Z operator + (Z a, Z b) { int x; return Z((x = a.v + b.v) >= Mod ? x - Mod : x); } friend Z operator - (Z a, Z b) { int x; return Z((x = a.v - b.v) < 0 ? x + Mod : x); } friend Z operator * (Z a, Z b) { return 1ll * a.v * b.v; } friend Z operator ~ (Z a) { _assert(a.v); return inv(a.v); } friend Z operator - (Z a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } }; typedef Z Zi; Zi qpow(Zi a, int p) { if (p < 0) p += Mod - 1; Zi rt = 1; for ( ; p; p >>= 1, a *= a) { if (p & 1) { rt *= a; } } return rt; } Zi qpow(Zi a, ll p) { return qpow(a, (int) (p % (Mod - 1))); } const int N = 405; int n, K; vector<Zi> fac, _fac; void init_fac(int n) { fac.resize(n + 1); _fac.resize(n + 1); fac[0] = 1; for (int i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i; } _fac[n] = ~fac[n]; for (int i = n; i; i--) { _fac[i - 1] = _fac[i] * i; } } Zi comb(int n, int m) { return n < m ? 0 : fac[n] * _fac[m] * _fac[n - m]; } Zi f[205][205]; Zi calc(int n, int m, int bu, int bd) { for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { f[i][j] = 0; } } for (int j = 0; j < m; j++) { f[0][j] = (j <= bu && j >= bd); } for (int i = 1; i < n; i++) { Zi sum = 0; for (int j = 0; j < m && j <= bu + i; j++) { sum += f[i - 1][j]; if (j >= bd + i) { f[i][j] = sum; } } } Zi sum = 0; for (int j = max(bd + n - 1, 0); j < m && j < bu + n; j++) { sum += f[n - 1][j]; } for (int i = 1; i < n; i++) { if (i + bu >= m) { sum += f[i][m - 1]; } } return sum; } int main() { scanf("%d%d%d", &n, &K, &Mod); init_fac(405); K = n - 1 - K; Zi ans = !K; for (int sl = max(K, 1); sl < n; sl++) { for (int sr = max(K, 1); sl + sr < n; sr++) { Zi tmp = calc(sr, sl, sl - K, K - sr); // cerr << tmp.v << " "; tmp = tmp * ~Zi(sl + sr + 1); ans += tmp; } } ans *= fac[n]; printf("%d\n", ans.v); return 0; }