CF1089I Interval-Free Permutations
CF1089I Interval-Free Permutations
题目大意
蔡老板是至高无上的世界首富,他手下有 \(n\) 名小弟(二五仔),编号为 \(1\dots n\)。这些二五仔站成一排。从左到右,他们的编号构成了一个排列 \(p_1, p_2, \dots p_n\)。
对于排列的一个子段 \(p_l, p_{l+1}, \dots, p_{r-1}, p_{r}\),如果 \(1 < r - l + 1 < n\),且将这个子段排序后是连续的一段数值,则这个子段里的二五仔们会叛变。
蔡老板要培养忠肝义胆的二五仔,因此不希望手下叛变。请你帮蔡老板数一数:有多少个长度为 \(n\) 的排列,满足没有子段会叛变呢?答案对一个质数 \(P\) 取模。
本题一个测试点里有 \(T\) 组测试数据,但他们的模数 \(P\) 相同。
数据范围:\(1\leq T\leq 400\),\(10^8 \leq P\leq 10^9\),\(1\leq n\leq 400\)。
本题题解
考虑用总数(\(n!\))减去不合法的排列数。我们现在要研究不合法的排列长什么样。
称【将子段排序后是连续的一段数值】的子段(也就是原题题面中的 interval)为不合法子段。那么合法的排列,就是不存在长度在 \([2, n - 1]\) 中的不合法子段的排列。
称一个不合法子段是极长的,当且仅当不存在另一个长度小于 \(n\) 的不合法子段包含它。发现,两个不合法子段如果有交,则它们的并也是不合法子段。因此,不合法的排列只有如下两种可能:
- 由两段极长不合法子段组成,这两段不合法子段可能相交。形式化地,若这两个子段分别是 \([l_1, r_1], [l_2, r_2]\),则 \(l_1 = 1, r_2 = n\),且 \(r_1\geq l_2 - 1\)。
- 由三段及以上的极长不合法子段拼成,它们互不相交。形式化地,若这些子段分别是 \([l_1, r_2], [l_2, r_2], \dots [l_k, r_k]\),则 \(l_1 = 1, r_k = n\),且 \(\forall 1\leq i < k: r_i + 1 = l_{i + 1}\)。
上述两种情况,不重不漏地刻画了所有不合法排列。因此只需要对这两种情况分别计数。
设长度为 \(i\) 的合法排列数为 \(f(i)\),也就是答案。
第一种不合法排列:发现两个子段中,必有一个包含的数值为 \(1, 2, \dots, i\)(其中 \(i\) 是子段长度)。不妨假设它是左边的子段,然后把方案数乘以 \(2\) 即可。设 \(h(i)\) 表示有多少长度为 \(i\) 的排列,满足它的任何长度 \(< i\) 的前缀,不是【数值的一个前缀】。形式化地 \(\forall 1\leq j < i: \{p_k | k \leq j\}\neq \{1\dots j\}\)。求 \(h(i)\),可以继续使用【总数减不合法数量】的思想:\(h(i) = i! - \sum_{j = 1}^{i - 1}h(j)\cdot (i - j)!\)。求出 \(h(i)\) 后,第一种不合法的排列数量是 \(2\cdot \sum_{i = 1}^{n - 1} h(i)\cdot (n - i)!\)。也就是枚举了左边子段的长度,后面的数可以任意排列:因为不管左边怎么样,后面任意排列,至少自己内部是一个不合法子段(\(i + 1\dots n\),连续的数值);当然,它可能还能向左延伸一点,也就是两个子段有交。
第二种不合法排列:考虑设 \(g(i, j)\) 表示 \(i\) 个数,划分为 \(j\) 个不合法子段的方案数(不一定极长)。则:\(g(i, j) = \sum_{k = 1}^{i}g(i - k, j - 1)\cdot k!\),其中枚举的 \(k\) 是最后一个不合法子段的长度(这个子段内部可以任意排列,也就是 \(k!\) 种方案)。边界是 \(g(0, 0) = 1\)。注意,这里没有考虑子段的相对顺序,也就是最后一个子段里的数值,默认为 \(i - k + 1\dots i\)。给此时的 \(j\) 个子段依次编号为 \(1\dots j\)。现在要求 \(j\) 个子段都是极长的,那么相当于 \(1\dots j\) 这些子段的子段,不能有连续数值,也就是:\(\sum_{j = 3}^{n - 1}g(n, j)\cdot f(j)\)。
综上所述:
预处理答案,时间复杂度 \(\mathcal{O}(n^3)\)。
参考代码
// problem: CF1089I
#include <bits/stdc++.h>
using namespace std;
#define mk make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
const int MAXN = 400;
int MOD;
inline int mod1(int x) { return x < MOD ? x : x - MOD; }
inline int mod2(int x) { return x < 0 ? x + MOD : x; }
inline void add(int &x, int y) { x = mod1(x + y); }
inline void sub(int &x, int y) { x = mod2(x - y); }
int fac[MAXN + 5];
int h[MAXN + 5];
int g[MAXN + 5][MAXN + 5];
int f[MAXN + 5];
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) {
fac[i] = (ll)fac[i - 1] * i % MOD;
}
h[1] = 1;
for (int i = 2; i <= n; ++i) {
h[i] = fac[i];
for (int j = 1; j < i; ++j) {
sub(h[i], (ll)h[j] * fac[i - j] % MOD);
}
}
g[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
for (int k = 1; k <= i; ++k) {
add(g[i][j], (ll)g[i - k][j - 1] * fac[k] % MOD);
}
}
}
f[1] = 1; f[2] = 2;
for (int i = 3; i <= n; ++i) {
f[i] = 0;
for (int j = 1; j < i; ++j) {
add(f[i], (ll)h[j] * fac[i - j] % MOD);
}
f[i] = mod1(f[i] * 2);
for (int j = 3; j < i; ++j) {
add(f[i], (ll)g[i][j] * f[j] % MOD);
}
f[i] = mod2(fac[i] - f[i]); // 总 - 不合法
}
}
int main() {
int T;
cin >> T;
cin >> MOD;
init(MAXN);
while (T--) {
int n;
cin >> n;
cout << f[n] << endl;
}
return 0;
}