题解 - QOJ 7766 栞

简述题意

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

解析

f(p)=qf(p)=q

刻画 ff 函数的过程

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

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

逐位考虑 qiq_i。以第 ii 个数为止分段的条件是:q1qiq_1\sim q_i 恰好落在 pp1i1\sim i 位。

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

部分分:qi=iq_i=i

pp 可被划分为至少 kk 段,其中每段 plprp_l\sim p_r 排序后恰好为 lrl\sim r。(即题意)

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

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

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

答案则为 kinfn,i\sum_{k\le i\le n}f_{n,i}

一般的 qq

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

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

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

所以我们找到 tt(在非部分分情况下必然存在)后,枚举每个 tt',对应的贡献就是 ft,k(nt)(tt1)!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/2024/02/17/sol-QOJ7766/
作者
SunsetGlow95
发布于
2024年2月17日
许可协议