归档: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
posted @ 2022-08-08 20:48  XSC062  阅读(30)  评论(0编辑  收藏  举报