组合数学杂记 2025.7.25始

末日后组合

1.1 鸽巢原理

1.2 排列组合

1.2.1 洛谷P7322 「PMOI-4」排列变换

给定常数 \(k\)。对于一个长度为 \(n\)排列 \(a\),定义

\[f(a)=\{\max_{1 \le i \le k} \{a_i\},\max_{2 \le i \le k+1} \{a_i\},\cdots,\max_{n-k+1 \le i \le n} \{a_i\}\} \]

对于一个长度为 \(n\)序列 \(a\),定义其权值 \(w(a)\)\(a\) 中不同的数的个数。

现在,\(\text{ducati}\) 想知道,对于所有长度为 \(n\) 的排列 \(p\),它们的 \(w(f(p))\) 之和。

省流:对于所有排列,其 \(k-max\) 滑动窗口所求得的最大值有多少种。

考虑什么时候会产生 不一样 的最大值。

显然有两种情况:

  1. 最大值移出去
  2. 更大值移进来

又可以发现这两种情况其实是等价的:都是最大值后面跟一段区间。

所以考虑一种情况再乘 \(2\) 就行。

假设第一种情况的描述:任选一个数 \(i\),始其左边 \(k\) 个数都比 \(i\) 小。

\(k\) 个数是从 \([1,i-1]\) 选出来的,并且可以随意排列。

\(i\) 可以在 \([k+1,n]\) 的位置。

剩下 \(n-k+1\) 个数可以随意排列。

\[2 \times \sum_{i=1}^n{\binom{i-1}{k} \times k! \times (n-k) \times (n-k+1)!} \]

这是突变的情况,但原始最大值还需要 \(+1\)

答案:

\[\big(2 \times \sum_{i=1}^n{\binom{i-1}{k} \times k! \times (n-k) \times (n-k+1)!} \big) + n! \]

但是我们有手癖,还想继续推式子(?

\[\begin{align} ori &= \big(2 \times \sum_{i=1}^n{\binom{i-1}{k} \times k! \times (n-k) \times (n-k+1)!} \big) + n! \\ &= \big(2 \times \sum_{i=1}^n{\binom{i-1}{k} \times k! \times (n-k)!} \big) + n! \\ &= \big(2 \times \sum_{i=1}^n{\frac{(i-1)!}{k! \times (i-1-k)!} \times k! \times (n-k)!} \big) + n! \\ &= \big(2 \times \sum_{i=1}^n{\frac{(i-1)!}{(i-1-k)!} \times (n-k)!} \big) + n! \end{align} \]

看起来推不动了,但我们休息一下继续~

\[\begin{align} ori &= \big(2 \times \sum_{i=1}^n{\frac{(i-1)!}{(i-1-k)!} \times (n-k)!} \big) + n! \\ &= 2 \times (n-k)! \times \sum_{i=1}^n{\frac{(i-1)!}{(i-1-k)!}} + n! \\ &= 2 \times (n-k)! \times k! \times \sum_{i=1}^n{\frac{(i-1)!}{(i-1-k)! \times k!}} + n! \\ &= 2 \times (n-k)! \times k! \times \sum_{i=1}^n{\binom{i-1}{k}} + n! \end{align} \]

我们插播两个引理:

\(\textbf{Lemma 1.} \dbinom{i}{j} = \dbinom{i-1}{j} + \dbinom{i-1}{j-1}\)

\(\textbf{Lemma 2.} \sum_{i-1}^n \dbinom{i-1}{j} = \dbinom{n}{j+1}\)

我不证(嘻嘻。

\[\begin{align} ori &= 2 \times (n-k)! \times k! \times \sum_{i=1}^n{\binom{i-1}{k}} + n! \\ &= 2 \times (n-k)! \times k! \times \dbinom{n}{k+1} + n! \\ &= 2 \times (n-k)! \times k! \times \frac{n!}{(n-k-1)! \times (k+1)!} + n! \\ &= \frac{2 \times (n-k) \times n!}{k+1} + n! \end{align} \]

\(\huge \mathscr{Code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 100, MOD = 998244353;
int n, k, fac, rec, ans;
int qpow(int a, int b) {
	if (b == 1) return a % MOD;
	int s = qpow(a, b >> 1);
	return b & 1 ? s * s % MOD * a % MOD : s * s % MOD;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> k;
	fac = 1;
	for (int i = 2; i <= n; i++) fac = fac * i % MOD;
	ans = (2 * fac * (n - k) % MOD * qpow(k + 1, MOD - 2) + fac + MOD) % MOD;
	cout << ans;
	return 0;
}

1.2.2 洛谷P6475 [NOI Online #2 入门组] 建设城市

球球是一位建筑师。一天,他收到市长的任务:建设城市。球球打算建造 \(2n\) 座高楼。为了保证城市美观,球球做出了如下计划:

  • 球球喜欢整齐的事物。他希望高楼从左向右排成一行,编号依次为 \(1\sim 2n\)

  • 球球喜欢整数,他要求每座高楼的高度都是正整数。

  • 由于材料限制,高楼的高度无法超过 \(m\)

  • 球球喜欢中间高,两边低的造型。他要求前 \(n\) 座高楼的高度不下降,后 \(n\) 座高楼的高度不上升。

  • 球球打算选两座编号为 \(x,y\) 的高楼作为这座城市的地标。他认为只有当这两座高楼高度相等时,才会让城市变得美观。

球球把自己的想法告诉了市长。市长希望得知所有建设城市的方案数。两种方案不同,当且仅当某座高楼的高度在两个方案中不同。这个问题可难倒了球球。球球找到了你,希望你能帮他算出答案。由于答案可能很大,你只需要给出答案对 \(998244353\) 取模后的结果。

考虑如果有 \(a\) 栋楼,\(b\) 种高度,有几种方案。

可以看作插板法,将 \(a\) 个小球放进 \(b\) 个盒子的方案数,注意此时允许盒子内不放小球。

如果是单增的,我们就有 \(\dbinom{a-1}{b-1}\),但是这是单调不降的,就有 \(\dbinom{a+b-1}{b-1}\)

接着对于 \(x,y\) 的高度,从 \(1\) 枚举到 \(m\),这时将整个序列劈成几段,分别用插板法计算。

\(\huge \mathscr{Code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 100, MOD = 998244353;
int n, m, x, y, ans;
int fac[N], inv[N], facinv[N];
void pre() {
	facinv[0] = 1;
	inv[1] = 1, fac[1] = 1, facinv[1] = 1;
	for (int i = 2; i <= N - 100; i++) {
		fac[i] = fac[i - 1] * i % MOD;
		inv[i] = (-(MOD / i) * inv[MOD % i] % MOD + MOD) % MOD;
		facinv[i] = facinv[i - 1] * inv[i] % MOD;
	}
}
int C(int n, int m) {
	if (n == 0 or m == 0) return 1;
	return fac[n] * facinv[m] % MOD * facinv[n - m] % MOD;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> m >> n >> x >> y;
	pre();
	if (x <= n && y > n) {
		for (int i = 1; i <= m; i++) {
			int k = C(x - 1 + i - 1, i - 1) * C(n - x + m - i, m - i) % MOD * C(2 * n - y + i - 1, i - 1) % MOD * C(y - n - 1 + m - i, m - i)% MOD;
			ans = (ans + k) % MOD;
		}
	}
	else if (x <= n) {
		for (int i = 1; i <= m; i++) {
			int k = C(x - 1 + i - 1, i - 1) * C(n - y + m - i, m - i) % MOD * C(n + m - 1, m - 1) % MOD;
			ans = (ans + k) % MOD;
		}
	}
	else {
		for (int i = 1; i <= m; i++) {
			int k = C(n + m - 1, m - 1) * C(2 * n - y + i - 1, i - 1) % MOD * C(x - n - 1 + m - i, m - i) % MOD;
			ans = (ans + k) % MOD;
		}
	}
	cout << ans;
	return 0;
}

1.3 卢卡斯定理

1.4 容斥原理

1.5 特殊数列(\(\textbf{catlan \& stirling}\)

1.6 \(\textbf{Burnside}\) 定理与 \(\textbf{Polya}\) 计数

1.7 母函数

1.8 博弈论

posted @ 2025-07-25 16:00  OrangeRED  阅读(20)  评论(2)    收藏  举报