A.No Attacking(贪心)
题意:
有一个NNN行NNN列的棋盘。用(i,j)(i,j)(i,j)表示从上往下数第iii行、从左往右数第jjj列的方格。现在你要在棋盘上放置棋子。有两种类型的棋子,分别叫做车和兵。当满足以下条件时,放置的棋子被称为良好布局:
- 每个方格上最多只能放置一个棋子。
- 如果(i,j)(i,j)(i,j)位置有一辆车,则对于所有k(1≤k≤N)k(1≤k≤N)k(1≤k≤N)且k≠jk≠jk=j,(i,k)(i,k)(i,k)位置不能有其他棋子;同样地,如果(i,j)(i,j)(i,j)位置有一辆车,则对于所有k(1≤k≤N)k(1≤k≤N)k(1≤k≤N)且k≠ik≠ik=i,(k,j)(k,j)(k,j)位置不能有其他棋子;
- 如果(i,j)(i,j)(i,j)位置是一个兵,并且i≥2i≥2i≥2,则(i−1,j)(i−1,j)(i−1,j)位置不能有其他棋子。
是否可能将AAA个车和BBB个兵以良好布局方式全部放置在这个棋盘上?
分析:
题目的良好布局要求两个条件,先考虑解决其中一个条件。若(i,j)(i,j)(i,j)位置是一个兵,则被约束的只有两个格子,如果是一个车,则被约束的是一行和一列。所以选择先把棋盘放满兵,接下来再去放车(全部放对角线上方便处理),放在没有兵的格子上,减少了333个兵;放在一个原本有兵的格子上,则会减少777个兵。所以先尽可能使用没有棋子的格子放车,没地方放了再放到原本有兵的格子上。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
bool flag = false;
int n, a, b;
cin >> n >> a >> b;
int all = (n) * (n / 2 + n % 2);
int cost = n / 2 + n % 2;
int tmp = n / 2;
if (a <= tmp) {
all -= cost * a;
} else {
flag = true;
cost -= a - tmp;
}
if (flag == false) {
if (all >= b)
cout << "Yes" << endl;
else
cout << "No" << endl;
} else {
if (cost < 0)
cout << "No" << endl;
else {
if (cost * cost >= b)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
}
return 0;
}
B.Chmax(数学)
题意:
对于(1,2,…,N)(1,2,…,N)(1,2,…,N)的排列P=(P1,P2,…,PN)P=(P_1,P_2,…,P_N)P=(P1,P2,…,PN),我们通过以下过程定义F(P)F(P)F(P):有一个序列 B=(1,2,…,N)B=(1,2,…,N)B=(1,2,…,N)。只要存在整数iii满足Bi<PBiB_i\lt P_{B_i}Bi<PBi,执行以下操作:令jjj是最小的满足Bi<PBiB_i\lt P_{B_i}Bi<PBi的整数iii。然后,用PBjP_{B_j}PBj替换BjB_jBj。
将F(P)F(P)F(P)定义为此过程结束时的序列BBB。(可以证明该过程在有限步骤后终止。)
给定长度为NNN的序列A=(A1,A2,…,AN)A=(A_1,A_2,…,A_N)A=(A1,A2,…,AN)。有多少个(1,2,…,N)(1,2,…,N)(1,2,…,N)的排列PPP满足F(P)=AF(P)=AF(P)=A?答案对998244353998244353998244353取模。
分析:
分析题面中的操作可以发现,其实是将所有满足Pi>iP_i>iPi>i的i→Pii→P_ii→Pi连边,得到若干条链,然后BiB_iBi即为iii所在链的最后一个节点。
显然,存在Ai<iA_i\lt iAi<i时无解,存在Ai≠iA_i≠iAi=i但Aj=iA_j=iAj=i时也无解。每个Ai≠iA_i≠iAi=i的位置填的数都唯一确定了(必须是下一个满足Aj=AiA_j=A_iAj=Ai的jjj),只需计算将剩下的数填入PPP中,且满足Pi<iP_i\lt iPi<i的方案数。具体方法为从小到大枚举iii,统计有多少个不超过iii的数还没有用过,若当前位置需要填数,就从这些数中选择一个填入。使用乘法原理统计即可得到答案。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 2e5 + 10;
LL n, a[N], ans = 1, b[N], cnt, flag[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
if (a[i] < i || a[a[i]] != a[i]) {
cout << 0 << endl;
return 0;
}
}
for (int i = n; i >= 1; i--) {
if (!b[a[i]]) b[a[i]] = i;
else {
int tmp = a[i];
a[i] = b[a[i]];
b[tmp] = i;
flag[a[i]] = true;
}
}
for (int i = 1; i <= n; i++) {
if (!flag[i]) cnt++;
if (a[i] == i) {
ans *= cnt;
ans %= mod;
cnt--;
}
}
cout << ans << endl;
return 0;
}
C.Swap on Tree(树形DP)
题意:
有一棵具有NNN个顶点的树,编号从111到NNN。第iii条边连接了顶点uiu_iui和viv_ivi。此外,还有NNN个编号为111到NNN的片段。最初,片段iii放置在顶点iii上。可以任意次数(可能为零)执行以下操作:
- 选择一条边。让uuu和vvv分别是该边的端点,并交换顶点uuu和vvv上的片段。然后删除所选边。
设aia_iai是位于顶点iii上的片段。完成操作后存在多少种不同可能的序列(ai,a2,...,aN)(a_i,a_2,...,a_N)(ai,a2,...,aN)?答案对998244353998244353998244353取模。
分析:
采用树形动态规划思路。当取一个根节点时,在不同子树之间交换的顶点最多只有111个。因此进行如下动态规划:dp[v][k]dp[v][k]dp[v][k]←顶点v,kv,kv,k。复杂度为O(N2)O(N^2)O(N2)。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
template<int mod>
struct Fp {
LL val;
constexpr Fp() : val(0) {}
constexpr Fp(LL v) : val(v % mod) {
if (val < 0)
val += mod;
}
constexpr LL get() const { return val; }
constexpr int get_mod() const { return mod; }
constexpr Fp operator+() const { return Fp(*this); }
constexpr Fp operator-() const { return Fp(0) - Fp(*this); }
constexpr Fp operator+(const Fp &r) const { return Fp(*this) += r; }
constexpr Fp operator-(const Fp &r) const { return Fp(*this) -= r; }
constexpr Fp operator*(const Fp &r) const { return Fp(*this) *= r; }
constexpr Fp operator/(const Fp &r) const { return Fp(*this) /= r; }
constexpr Fp &operator+=(const Fp &r) {
val += r.val;
if (val >= mod)
val -= mod;
return *this;
}
constexpr Fp &operator-=(const Fp &r) {
val -= r.val;
if (val < 0)
val += mod;
return *this;
}
constexpr Fp &operator*=(const Fp &r) {
val = val * r.val % mod;
return *this;
}
constexpr Fp &operator/=(const Fp &r) {
LL a = r.val, b = mod, u = 1, v = 0;
while (b) {
LL t = a / b;
a -= t * b, swap(a, b);
u -= t * v, swap(u, v);
}
val = val * u % mod;
if (val < 0)
val += mod;
return *this;
}
constexpr Fp pow(LL n) const {
Fp res(1), mul(*this);
while (n > 0) {
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
constexpr Fp inv() const {
Fp res(1), div(*this);
return res / div;
}
constexpr bool operator==(const Fp &r) const {
return this->val == r.val;
}
constexpr bool operator!=(const Fp &r) const {
return this->val != r.val;
}
constexpr Fp &operator++() {
++val;
if (val >= mod)
val -= mod;
return *this;
}
constexpr Fp &operator--() {
if (val == 0)
val += mod;
--val;
return *this;
}
constexpr Fp operator++(int) const {
Fp res = *this;
++*this;
return res;
}
constexpr Fp operator--(int) const {
Fp res = *this;
--*this;
return res;
}
friend constexpr istream &operator>>(istream &is, Fp<mod> &x) {
is >> x.val;
x.val %= mod;
if (x.val < 0)
x.val += mod;
return is;
}
friend constexpr ostream &operator<<(ostream &os, const Fp<mod> &x) {
return os << x.val;
}
friend constexpr Fp<mod> pow(const Fp<mod> &r, LL n) {
return r.pow(n);
}
friend constexpr Fp<mod> inv(const Fp<mod> &r) {
return r.inv();
}
};
template<class mint>
struct BiCoef {
vector<mint> fact_, inv_, finv_;
constexpr BiCoef() {}
constexpr BiCoef(int n) : fact_(n, 1), inv_(n, 1), finv_(n, 1) {
init(n);
}
constexpr void init(int n) {
fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1);
int mod = fact_[0].get_mod();
for (int i = 2; i < n; i++) {
fact_[i] = fact_[i - 1] * i;
inv_[i] = -inv_[mod % i] * (mod / i);
finv_[i] = finv_[i - 1] * inv_[i];
}
}
constexpr mint com(int n, int k) const {
if (n < k || n < 0 || k < 0)
return 0;
return fact_[n] * finv_[k] * finv_[n - k];
}
constexpr mint fact(int n) const {
if (n < 0)
return 0;
return fact_[n];
}
constexpr mint inv(int n) const {
if (n < 0)
return 0;
return inv_[n];
}
constexpr mint finv(int n) const {
if (n < 0)
return 0;
return finv_[n];
}
};
using mint = Fp<mod>;
int main() {
int N;
cin >> N;
vector<vector<int>> G(N);
for (int i = 0; i < N - 1; ++i) {
int u, v;
cin >> u >> v;
--u, --v;
G[u].push_back(v);
G[v].push_back(u);
}
BiCoef<mint> bc(N + 1);
vector<int> siz(N, 0);
vector<vector<mint>> dp(N, vector<mint>(N, 0));
vector<mint> dpsum(N, 0);
auto rec = [&](auto rec, int v, int p) -> void {
int cnt = 0;
for (auto ch: G[v]) {
if (ch == p)
continue;
rec(rec, ch, v);
++cnt;
}
siz[v] = 1;
vector<mint> dp2(cnt + 2, 0);
dp2[1] = 1;
for (auto ch: G[v]) {
if (ch == p)
continue;
siz[v] += siz[ch];
vector<mint> nex(cnt + 2, 0);
for (int k = 0; k <= cnt + 1; ++k) {
nex[k] += dp2[k] * dpsum[ch];
for (int l = 1; l <= siz[ch]; ++l) {
if (k + 1 <= cnt + 1) nex[k + 1] += dp2[k] * dp[ch][l] * l;
}
}
swap(dp2, nex);
}
dpsum[v] = 0;
for (int k = 1; k <= cnt + 1; ++k) {
dp[v][k] = dp2[k] * bc.fact(k - 1);
dpsum[v] += dp[v][k];
}
};
rec(rec, 0, -1);
cout << dpsum[0] << endl;
return 0;
}
D.Rolling Hash(着色问题)
题意:
给定非负整数PPP和BBB。PPP是素数,1≤B≤P−11≤B≤P−11≤B≤P−1。对于一个非负整数序列X=(x1,x2,…,xn)X=(x_1,x_2,…,x_n)X=(x1,x2,…,xn),哈希值hash(X)hash(X)hash(X)定义如下。hash(X)=(∑i=1nxiBn−i)mod Phash(X)=(\sum\limits_{i=1}^{n}x_i B^{n-i})\mod Phash(X)=(i=1∑nxiBn−i)modP。给定MMM对整数(L1,R1)(L_1,R_1)(L1,R1),(L2,R2)(L_2,R_2)(L2,R2),…,(LM,RM)(L_M,R_M)(LM,RM)。是否存在长度为NNN的非负整数序列A=(A1,A2,…,AN)A=(A_1,A_2,…,A_N)A=(A1,A2,…,AN)满足以下条件:
- 对于所有的i(1≤i≤M)i(1≤i≤M)i(1≤i≤M),满足以下条件:设sss是由取出AAA中第LiL_iLi到第RiR_iRi元素得到的序列(ALi,ALi+1,…,ARi)(A_{L_i},A_{L_i+1},…,A_{R_i})(ALi,ALi+1,…,ARi)。那么,hash(s)≠0hash(s)≠0hash(s)=0.
分析:
首先,重新表述问题条件。定义B′≡B−1B^′≡B−1B′≡B−1,定义数列AAA为Ci=AiB′iC_i=A_i B^{′i}Ci=AiB′i。进一步定义CCC的和为SSS。这时候,可以发现问题条件等价于对每个(Li,Ri)(L_i,R_i)(Li,Ri),满足SRi+1S_{R_i+1}SRi+1KaTeX parse error: Unknown accent ' ̸' at position 1: ≢̲̲ SLiS_{L_i}SLi(mod P)(\mod P)(modP)。因为在mod P\mod PmodP下,与BBB进行乘除仍保持等价性。特别地,在P>NP>NP>N时总会成立。例如即使对所有区间都有限制条件,只需取S0≡0S_0≡0S0≡0、S1≡1S_1≡1S1≡1、S2≡2S_2≡2S2≡2…SN≡NS_N≡NSN≡N即可将满足要求的数列AAA很容易逆推得出。
一般情况下,数列SSS和数列AAA之间仍然存在一对一关系不变。因此可以判断是否可能按照以下条件分配各个值来满足S0,S1,…,SNS_0,S_1,…,S_NS0,S1,…,SN:将序列SiS_iSi中每个值设定为大于等于000且小于等于P−1P−1P−1;同时设定S0≡0S_0≡0S0≡0;并确保对每个(Li,Ri)(L_i,R_i)(Li,Ri),SRi+1≢SLimod PS_{R_i+1}≢S_{L_i} \mod PSRi+1≢SLimodP成立。
仔细想想就会发现这其实是一个着色问题。需要计算由顶点0,1,…,N0,1,…,N0,1,…,N组成的MMM条边(Li,Ri+1)(L_i,R_i+1)(Li,Ri+1)构成的图的着色数,并判断其是否小于等于PPP。
代码:
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
template<int mod>
struct Fp {
LL val;
constexpr Fp() : val(0) {}
constexpr Fp(LL v) : val(v % mod) {
if (val < 0) val += mod;
}
constexpr LL get() const { return val; }
constexpr int get_mod() const { return mod; }
constexpr Fp operator+() const { return Fp(*this); }
constexpr Fp operator-() const { return Fp(0) - Fp(*this); }
constexpr Fp operator+(const Fp &r) const { return Fp(*this) += r; }
constexpr Fp operator-(const Fp &r) const { return Fp(*this) -= r; }
constexpr Fp operator*(const Fp &r) const { return Fp(*this) *= r; }
constexpr Fp operator/(const Fp &r) const { return Fp(*this) /= r; }
constexpr Fp &operator+=(const Fp &r) {
val += r.val;
if (val >= mod)
val -= mod;
return *this;
}
constexpr Fp &operator-=(const Fp &r) {
val -= r.val;
if (val < 0)
val += mod;
return *this;
}
constexpr Fp &operator*=(const Fp &r) {
val = val * r.val % mod;
return *this;
}
constexpr Fp &operator/=(const Fp &r) {
LL a = r.val, b = mod, u = 1, v = 0;
while (b) {
LL t = a / b;
a -= t * b, swap(a, b);
u -= t * v, swap(u, v);
}
val = val * u % mod;
if (val < 0)
val += mod;
return *this;
}
constexpr Fp pow(LL n) const {
Fp res(1), mul(*this);
while (n > 0) {
if (n & 1)
res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
constexpr Fp inv() const {
Fp res(1), div(*this);
return res / div;
}
constexpr bool operator==(const Fp &r) const {
return this->val == r.val;
}
constexpr bool operator!=(const Fp &r) const {
return this->val != r.val;
}
constexpr Fp &operator++() {
++val;
if (val >= mod)
val -= mod;
return *this;
}
constexpr Fp &operator--() {
if (val == 0)
val += mod;
--val;
return *this;
}
constexpr Fp operator++(int) const {
Fp res = *this;
++*this;
return res;
}
constexpr Fp operator--(int) const {
Fp res = *this;
--*this;
return res;
}
friend constexpr istream &operator>>(istream &is, Fp<mod> &x) {
is >> x.val;
x.val %= mod;
if (x.val < 0)
x.val += mod;
return is;
}
friend constexpr ostream &operator<<(ostream &os, const Fp<mod> &x) {
return os << x.val;
}
friend constexpr Fp<mod> pow(const Fp<mod> &r, LL n) {
return r.pow(n);
}
friend constexpr Fp<mod> inv(const Fp<mod> &r) {
return r.inv();
}
};
int chromatic_number(const vector<vector<int>> &G) {
using mint = Fp<mod>;
int n = (int) G.size();
vector<int> neighbor(n, 0);
for (int i = 0; i < n; ++i) {
int S = (1 << i);
for (int j = 0; j < n; ++j) if (G[i][j]) S |= (1 << j);
neighbor[i] = S;
}
vector<int> I(1 << n);
I[0] = 1;
for (int S = 1; S < (1 << n); ++S) {
int v = __builtin_ctz(S);
I[S] = I[S & ~(1 << v)] + I[S & ~neighbor[v]];
}
int low = 0, high = n;
while (high - low > 1) {
int mid = (low + high) >> 1;
mint g = 0;
for (int S = 0; S < (1 << n); ++S) {
if ((n - __builtin_popcount(S)) & 1)
g -= mint(I[S]).pow(mid);
else
g += mint(I[S]).pow(mid);
}
if (g != 0)
high = mid;
else low = mid;
}
return high;
}
int main() {
LL P, B, N, M;
cin >> P >> B >> N >> M;
vector<vector<int>> G(N + 1, vector<int>(N + 1, 0));
for (int i = 0; i < M; ++i) {
int l, r;
cin >> l >> r;
--l;
G[l][r] = G[r][l] = 1;
}
if (chromatic_number(G) <= P)
cout << "Yes" << endl;
else
cout << "No" << endl;
return 0;
}
E.Rookhopper’s Tour
题意:
有一个NNN行NNN列的网格。用(i,j)(i,j)(i,j)表示从上到下第iii行,从左到右第jjj列的单元格。此外,有一颗黑色石头和MMM颗白色石头。你将使用这些物品进行游戏。以下是规则:最初,你将黑色石头放在(A,B)(A,B)(A,B)处。然后,你将每个白色石头放在网格的某个单元格上。注意:
- 不能把白色石头放在(A,B)(A,B)(A,B)处;
- 每行最多只能放置一个白色石头;
- 每列最多只能放置一个白色石头。
然后,直到无法继续操作为止,执行以下操作之一:
- 假设黑色石头位于(i,j)(i,j)(i,j),执行以下四种操作之一:
- 如果(i,k)(i,k)(i,k)处有一颗白色石头且(j<k)(j\lt k)(j<k),移除该白色石头并将黑色石头移动至(i,k+1)(i,k+1)(i,k+1);
- 如果(i,k)(i,k)(i,k)处有一颗白色石头且(j>k)(j>k)(j>k),移除该白色石头并将黑色石头移动至(i,k−1)(i,k−1)(i,k−1);
- 如果(k,j)(k,j)(k,j)处有一颗白色石头且(i<k)(i\lt k)(i<k),移除该白色石头并将黑色石头移动至(k+1,j)(k+1,j)(k+1,j);
- 如果(k,j)(k,j)(k,j)处有一颗白色石头且(i>k)(i>k)(i>k),移除该白色石头并将黑色石头移动至(k−1,j)(k−1,j)(k−1,j)。
在这里,如果要移动黑色石头的单元格不存在,则无法进行此操作。
当你完成操作时,只有满足以下所有条件时才能赢得游戏;否则你输了:
- 网格中的所有白色石头都被移除;
- 黑色石头放在(A,B)(A,B)(A,B)处。
通过执行操作,在多少种初始放置下可以赢得游戏?答案对998244353取模。
分析:
考虑白色石头的放置必须满足胜利条件的情况。首先,由于同一列中不能有多个白色石头,黑色石头必须按以下模式移动:(aaa类):垂直->水平->垂直->…->垂直->水平,或者(bbb类):水平->垂直->水平->…->水平->垂直。因此,MMM必须是偶数。
此外,可以证明对于满足条件的放置,只有一种类型的移动(aaa)或(bbb)是可能的。因此我们只需要计算每种类型的移动次数。考虑可以进行(aaa类)移动的放置方法,同时这种思路对(bbb类)也适用。我们考虑放置的特性。例如, 如果第一步向下走, 第二步向右走, 那么这些步骤如下所示: 跳过位于(x,B)(x,B)(x,B)处的白色石头到达(x+1,B)(x+1,B)(x+1,B), 然后跳过位于(x+1,y)(x+1,y)(x+1,y)处的白色石头到达(x+1,y+1)(x+1,y+1)(x+1,y+1)。这意味着在xxx行和(x+1)(x+1)(x+1)行都放置了石头。
类似地,在一般情况下下列关系成立(其中将第nnn步视为模MMM):设nnn是奇数,则在第nnn次和(n+1)(n+1)(n+1)次移动中移除的白色石头被放置在相邻的行上。此外,如果黑色石头从上方来,则第nnn次移动中的石头位于比(n+1)(n+1)(n+1)次移动中的石头更高的行;如果它从下方来,则位于较低行。
当nnn是偶数时,列也满足类似关系。因此,可以将石头的摆放解释为交替占据两行和两列的操作。
基于这些特性进行计数。简单起见, 首先考虑没有条件要求起始点(和结束点)是(A,B)(A,B)(A,B)的情况。 所有放置操作都可以表示为交替占据两行和两列的操作。相反地,如果固定行和列的顺序被确定,那么对应的起始点和白色石头的放置就是唯一确定的。这种一对一的对应关系使得问题可以简化为计算要占用行和列的顺序。行和列排序方式的数量可以用二项式系数表示,并且可以算出。
起始点为(A,B)(A,B)(A,B)的条件会对如何占用行和列施加以下限制:在第(M−1)(M-1)(M−1)次移动中移除的白色石头要么占据AAA行和(A+1)(A+1)(A+1)行,要么占据(A−1)(A-1)(A−1)行和AAA行(哪些行被占据取决于第(M−2)(M-2)(M−2)次移动中移除白色石头位置是否在AAA行之上或之下)。在第MMM次和第111次移动中移除的白色石头要么占据BBB列和(B+1)(B+1)(B+1)列,要么占据(B−1)(B-1)(B−1)列和BBB列(哪些列被占据取决于第(M−1)(M-1)(M−1)次移动中移除白色石头位置是否在BBB列右侧或左侧)。
该问题要求我们计算满足这些条件时行与列被占用的方式数量。通过固定第(M−2)(M-2)(M−2)次移动中移除的白色石头与AAA行之间以及第(M−1)(M-1)(M−1)次移动中移除的白色石头与BBB列之间的关系,总共有444种情况,分情况累加可以得出答案。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 2e5 + 10;
LL modinv(LL a, LL mod) {
if (a < 0) a %= mod;
if (a < 0) a += mod;
if (a >= mod) a %= mod;
assert(a);
LL x = 0, y = 1, u = 1, v = 0, b = mod;
while (b != 0) {
LL q = a / b;
a %= b;
swap(a, b);
u -= q * x;
swap(u, x);
v -= q * y;
swap(y, v);
}
if (u > 0)
return u;
else return u + mod;
}
vector<LL> fact;
vector<LL> invfact;
void set_fact(LL n, LL mod) {
fact.resize(n + 1, 1);
invfact.resize(n + 1, 1);
for (LL i = 2; i <= n; i++) {
fact[i] = fact[i - 1] * i % mod;
}
invfact[n] = modinv(fact[n], mod);
for (LL i = n - 1; i >= 2; i--) {
invfact[i] = invfact[i + 1] * (i + 1) % mod;
}
return;
}
LL comb(LL n, LL k, LL mod) {
if (k > n || k < 0) return 0;
if (k == n || k == 0) return 1;
return fact[n] * invfact[n - k] % mod * invfact[k] % mod;
}
int main() {
int N, M, A, B;
LL ans = 0;
cin >> N >> M >> A >> B;
set_fact(N + 10, mod);
if (M % 2) {
cout << 0 << endl;
return 0;
}
LL x = 0;
LL y = 0;
LL coef = modinv(M / 2 - 1, mod);
for (int a = 0; a <= M / 2 - 1; a++) {
int b = M / 2 - 1 - a;
int l = A - 1, r = N - A - 1;
if (a * 2 <= l && b * 2 <= r) {
LL summand = comb(l - a, a, mod) * comb(r - b, b, mod) % mod;
if (b > 0) x += summand * fact[M / 2 - 1] % mod * b % mod * coef % mod;
}
l = A - 2, r = N - A;
if (a * 2 <= l && b * 2 <= r) {
LL summand = comb(l - a, a, mod) * comb(r - b, b, mod) % mod;
if (a > 0) x += summand * fact[M / 2 - 1] % mod * a % mod * coef % mod;
}
int l1 = B - 1, r1 = N - B - 1;
if (a * 2 <= l1 && b * 2 <= r1) {
LL summand = comb(l1 - a, a, mod) * comb(r1 - b, b, mod) % mod;
if (b > 0) y += summand * fact[M / 2 - 1] % mod * b % mod * coef % mod;
}
l1 = B - 2, r1 = N - B;
if (a * 2 <= l1 && b * 2 <= r1) {
LL summand = comb(l1 - a, a, mod) * comb(r1 - b, b, mod) % mod;
if (a > 0) y += summand * fact[M / 2 - 1] % mod * a % mod * coef % mod;
}
}
x %= mod;
y %= mod;
ans += x * y % mod;
ans *= 2;
ans %= mod;
cout << ans << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

443

被折叠的 条评论
为什么被折叠?



