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;
}
浙公网安备 33010602011771号