# 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;
}


posted @ 2020-07-13 19:10  阿波罗2003  阅读(455)  评论(0编辑  收藏  举报