QOJ 7766 栞

简述题意

对于 1∼n1\sim n 的排列 pp,定义 f(p)f(p) 为把 pp 划分为 kk 连续段后每段分别排序、其中所有划分方法中所可能达成的字典序最小的排列。给定 f(p),kf(p),k,数 pp 的个数。1≤k≤n≤5001\le k\le n\le 500。

解析

令 f(p)=qf(p)=q。

刻画 ff 函数的过程

既然求字典序最小的排列,首先要 q1q_1 最小。q1=min⁡1≤i≤n−k+1piq_1=\min_{1\le i\le n-k+1}p_i。(这个上界是因为剩余的 k−1k-1 段非空)

接着要 q2q_2 最小,就是把 q1q_1 屏蔽掉后的最小值。

逐位考虑 qiq_i。以第 ii 个数为止分段的条件是:q1∼qiq_1\sim q_i 恰好落在 pp 的 1∼i1\sim i 位。

在前面能分段就分段,后面部分段数变少,字典序不增。而且既然已经分段,说明 ai+1∼an−k+1a_{i+1}\sim a_{n-k+1} 都大于 a1∼aia_1\sim a_i,所以也不会变劣。综上可说明此刻画正确性。

部分分:qi=iq_i=i

则 pp 可被划分为至少 kk 段,其中每段 pl∼prp_l\sim p_r 排序后恰好为 l∼rl\sim r。(即题意)

考虑记 fi,jf_{i,j} 为满足这样条件的 ii 阶排列个数:它最多刚好可以被划分成 jj 段,使每段 pl∼prp_l\sim p_r 排序后恰好为 l∼rl\sim r。

对于 fi,1f_{i,1} 的计算,方法多样,其中一种是考虑任意 i−1i-1 阶排列的第一个 xx 满足 p1∼pxp_1\sim p_x 排序后恰好为 1∼x1\sim x。要在值域中插入一个数使得 j=1j=1,则必有 pi′<px′p'_i<p'_x。所以有 fi,1=∑1≤x≤i−1fx,1x(i−x−1)!f_{i,1}=\sum_{1\le x\le i-1}f_{x,1}x(i-x-1)!。

对于其他 fi,jf_{i,j},直接 fi,j=∑1≤x≤jfx,1fi−x,j−1f_{i,j}=\sum_{1\le x\le j}f_{x,1}f_{i-x,j-1} 即可。(也就是生成函数卷积)

答案则为 ∑k≤i≤nfn,i\sum_{k\le i\le n}f_{n,i}。

一般的 qq

由上文对 ff 函数过程的刻画,可知一旦选中一个 pn−k+1p_{n-k+1},这一段以后每一段长度只能为 11。只要没有选中,由于 p1∼pn−kp_1\sim p_{n-k} 总是在 min⁡\min 备选范围里面,所以假设选中的是 t=n−kt=n-k,则有 ∀2≤i<t,pi−1<pi\forall 2\le i<t,p_{i-1}<p_i。然而选完 tt 之后,出于同样的原因,仍有 ∀i≥t,pi<pi+1\forall i\ge t,p_i<p_{i+1},并且 pt−1<pt+1p_{t-1}<p_{t+1}(在对应元素存在的前提下)。然而不保证 pt−1<ptp_{t-1}<p_t。

所以第一个出现 pi−1<pip_{i-1}<p_i 的 ii 只能是 tt(如果存在)。于是我们找到 t′t'(如果存在),它是 tt 之后的第一个满足 pt′−1>ptp_{t'-1}>p_t 的位置,这个位置开始每一段长度只能为 11。换言之,[t,t′)[t,t') 是最后一段长度可能不为 11 的分段,而且这必须是一段。

整理一下,整个序列应该形如这样:[1,t)[1,t) 是一个递增序列,被分成若干段;[t,t′)[t,t') 是一个递增序列,若它长度不为 11 则必有 pt−1<pt+1p_{t-1}<p_{t+1},并且这一区间独立成段;[t′,n][t',n] 是若干长度为 11 的段。

所以我们找到 tt(在非部分分情况下必然存在)后,枚举每个 t′t',对应的贡献就是 ft,k−(n−t′)(t′−t−1)!f_{t,k-(n-t')}(t'-t-1)!([t,t′)[t,t') 可以任意排列,[1,t)[1,t) 如部分分所示),求和即可。

代码

代码中下标从 00 开始,并且有若干命名和操作与公式等效而不同名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

const int MXN = 505;
const int DVS = 998244353;
int N, K, arr[MXN], cnt[MXN][MXN], Pt, ans;
bool type;
int main() {
cin >> N >> K;
for (int i(0); i != N; ++i) cin >> arr[i], --arr[i];
for (int i(1); i != N; ++i) {
if (arr[i] < arr[i - 1]) {
Pt = i + 1;
while (Pt != N && arr[Pt] > max(arr[Pt - 2], arr[Pt - 1])) ++Pt;
type = 1;
K -= N - Pt + 1;
N = i;
break;
}
}
if (K <= 0) {
cout << 0 << endl;
return 0;
}
cnt[1][1] = 1;
for (int i(2); i <= N; ++i) {
for (int j(i - 1), f(1); j >= 1; f = f * 1LL * (i - j--) % DVS) {
cnt[i][1] = (cnt[i][1] + cnt[j][1] * 1LL * f % DVS * j) % DVS;
}
for (int j(2); j <= i; ++j) {
for (int k(1); j - 1 <= i - k; ++k) {
cnt[i][j] = (cnt[i][j] + cnt[i - k][j - 1] * 1LL * cnt[k][1]) % DVS;
}
}
}
if (type) {
for (int i(0); i != Pt - N; ++i) {
int t(cnt[N][K - i]);
for (int j(1); j <= Pt - 1 - N - i; ++j) t = t * 1LL * j % DVS;
ans = (ans + t) % DVS;
}
} else {
for (int i(K); i <= N; ++i) ans = (ans + cnt[N][i]) % DVS;
}
cout << ans << endl;
return 0;
}

QOJ 7766 栞
http://sunsetglow95.github.io/sol-QOJ7766/
作者
SunsetGlow95
发布于
2024年2月17日
许可协议