组合数学杂记 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\) 滑动窗口所求得的最大值有多少种。
考虑什么时候会产生 不一样 的最大值。
显然有两种情况:
- 最大值移出去
- 更大值移进来
又可以发现这两种情况其实是等价的:都是最大值后面跟一段区间。
所以考虑一种情况再乘 \(2\) 就行。
假设第一种情况的描述:任选一个数 \(i\),始其左边 \(k\) 个数都比 \(i\) 小。
这 \(k\) 个数是从 \([1,i-1]\) 选出来的,并且可以随意排列。
\(i\) 可以在 \([k+1,n]\) 的位置。
剩下 \(n-k+1\) 个数可以随意排列。
这是突变的情况,但原始最大值还需要 \(+1\)。
答案:
但是我们有手癖,还想继续推式子(?
看起来推不动了,但我们休息一下继续~
我们插播两个引理:
\(\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}\)
我不证(嘻嘻。
\(\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;
}

浙公网安备 33010602011771号