归档:220808 | 开门劝退:排列组合系列题
Codeforces [1284C] - New Year and Permutation
算一个序列中有多少个合法数对是很麻烦的,那么不妨换个角度想,对于每个数对,计算能被多少个序列包含。
容易发现对于 \([l, r]\),设最小值为 \(l + v\),最大值为 \(r + v\),那么剩余的 \(r-l-1\) 个数既不能比 \(l+v\) 小,也不能比 \(r+v\) 大,还不能重复,那么就与 \((l+v,r+v)\) 对应。也就是说,左端点的值确定后,区间包含元素唯一确定。
对于所有长度为 \(i\) 的区间,左端点有 \(n-i+1\) 种选择,区间所在的位置有 \(n-i+1\) 种选择,区间可任意排列,不影响极差,所以有 \(A_i^i\) 种排列方式,除该区间以外的数有 \(A_{n-i}^{n-i}\) 种排列方式(这一步,因为每个数列的每个合法区间都要计算,所以不用对该区间之外的合法区间去重)。
乘法原理,全部相乘即可得到长度为 \(i\) 的所有区间的答案,枚举 \(i\),相加即可。
特别地,对于长度为 \(1\) 的区间,每个区间都被 \(A_n^n\) 个区间包含,共有 \(n\times A_n^n\) 个。
namespace XSC062 {
using namespace fastIO;
const int maxn = 2.5e5 + 15;
int n, mod, ans;
int u[maxn], inv[maxn];
inline int qkp(int x, int y) {
int res = 1;
while (y) {
if (y & 1)
(res *= x) %= mod;
(x *= x) %= mod;
y >>= 1;
}
return res;
}
inline void Init(int n) {
u[0] = 1;
inv[0] = 1;
for (int i = 1; i <= n; ++i) {
u[i] = (u[i - 1] * i) % mod;
inv[i] = qkp(u[i], mod - 2);
}
return;
}
inline int A(int n, int m) {
return (u[n] * inv[n - m]) % mod;
}
int main() {
read(n);
read(mod);
Init(n + 5);
ans = (A(n, n) * n) % mod;
for (int i = 2; i <= n; ++i) {
(ans += (((n - i + 1) * (n - i + 1)) % mod
* A(i, i)) % mod * A(n - i, n - i)
% mod) %= mod;
}
printf("%lld", ans);
return 0;
}
} // namespace XSC062
—— · EOF · ——
真的什么也不剩啦 😖