题目来源
Description
- 对于长度为 i ∈ [ 1 , n ] i\in[1,n] i∈[1,n] 的一个 [ 1 , i ] [1,i] [1,i] 的无序排列,求能够通过二分的方法查询到第 k ∈ [ 1 , i ] k\in[1,i] k∈[1,i] 小的元素的序列个数。
- 10 ≤ n ≤ 400 10\le n\le400 10≤n≤400,答案对给定的 2 ≤ p ≤ 998244353 2\le p\le998244353 2≤p≤998244353 取模( p p p 不一定为质数)。
Solution
首先我们考虑,若需要正确地找到第 k k k 小的元素(我们称之为 x x x),则对于每一个 M i d Mid Mid, a M i d a_{Mid} aMid 与 x x x 的大小关系是确定的。
换言之,若 x x x 位于位置 p p p,我们可以模拟二分的过程:
对于当前的 M i d Mid Mid,若 p < M i d p<Mid p<Mid 则必然 a M i d > x a_{Mid}>x aMid>x,若 p > M i d p>Mid p>Mid 则必然 a M i d < x a_{Mid}<x aMid<x,否则已经找到了 x x x。
那么我们就能求出 a a a 数列中,必须小于 x x x 以及必须大于 x x x 的数的个数,分别记作 c n t 1 cnt_1 cnt1 和 c n t 2 cnt_2 cnt2,则大小关系与 x x x 不确定的个数为 n − c n t 1 − c n t 2 − 1 n-cnt_1-cnt_2-1 n−cnt1−cnt2−1(总数减小于减大于减等于的个数),其中 n n n 为数列长度。则满足此大小关系的数列的个数为: A x − 1 c n t 1 × A n − x c n t 2 × A n − c n t 1 − c n t 2 − 1 n − c n t 1 − c n t 2 − 1 m o d p A_{x-1}^{cnt_1}\times A_{n-x}^{cnt_2}\times A_{n-cnt_1-cnt_2-1}^{n-cnt_1-cnt_2-1}\bmod p Ax−1cnt1×An−xcnt2×An−cnt1−cnt2−1n−cnt1−cnt2−1modp。
则我们可以设立三层循环:第一层循环
i
∈
[
1
,
n
]
i\in[1,n]
i∈[1,n] 表示数列长度,第二层循环
j
∈
[
1
,
i
]
j\in[1,i]
j∈[1,i] 表示需要找到第
j
j
j 大的元素,第三层循环
k
∈
[
1
,
i
]
k\in[1,i]
k∈[1,i] 表示第
j
j
j 大元素位于位置
k
k
k 时的方案数。若设
a
n
s
(
i
,
j
)
ans_{(i,j)}
ans(i,j) 为输出中第
i
i
i 行第
j
j
j 列,则
a
n
s
(
i
,
j
)
=
(
∑
k
=
1
i
A
j
−
1
c
n
t
1
×
A
i
−
j
c
n
t
2
×
A
i
−
c
n
t
1
−
c
n
t
2
−
1
i
−
c
n
t
1
−
c
n
t
2
−
1
)
m
o
d
p
ans_{(i,j)}=\Big(\sum_{k=1}^iA_{j-1}^{cnt_1}\times A_{i-j}^{cnt_2}\times A_{i-cnt_1-cnt_2-1}^{i-cnt_1-cnt_2-1}\Big)\bmod p
ans(i,j)=(k=1∑iAj−1cnt1×Ai−jcnt2×Ai−cnt1−cnt2−1i−cnt1−cnt2−1)modp
若我们暴力求
A
A
A,那么复杂度会达到
O
(
n
4
log
n
)
O(n^4\log n)
O(n4logn),显然不行;那么我们可以
O
(
n
2
)
O(n^2)
O(n2) 预处理出
∀
i
∈
[
1
,
n
]
,
j
∈
[
1
,
i
]
\forall i\in[1,n],j\in[1,i]
∀i∈[1,n],j∈[1,i],
C
i
j
C_i^j
Cij 的值,
O
(
n
)
O(n)
O(n) 预处理出
∀
i
∈
[
1
,
n
]
\forall i\in[1,n]
∀i∈[1,n],
i
!
i!
i! 的值,再结合排列数和阶乘
O
(
n
2
)
O(n^2)
O(n2) 预处理出
A
i
j
=
C
i
j
×
j
!
A_i^j=C_i^j\times j!
Aij=Cij×j! 降低复杂度即可。并且可以
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn) 预处理出
c
n
t
1
,
c
n
t
2
cnt_1,cnt_2
cnt1,cnt2 ,最终复杂度为
O
(
n
3
)
O(n^3)
O(n3)。
Code
#include <bits/stdc++.h>
using namespace std;
int p,n,C[500][500],frac[500]={1},A[500][500],cnt1[500][500],cnt2[500][500]; // cnt1表示<的个数 cnt2表示>的个数
int main(){
scanf("%d%d",&p,&n);
for (int i=1;i<=n;i++) frac[i]=1ll*frac[i-1]*i%p; // 预处理阶乘
for (int i=0;i<=n;i++) C[i][0]=C[i][i]=1;
for (int i=1;i<=n;i++) for (int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; // 预处理组合数
for (int i=0;i<=n;i++) for (int j=0;j<=i;j++) A[i][j]=1ll*C[i][j]*frac[j]%p; // 组合数*阶乘=排列数
for (int i=1;i<=n;i++) // 预处理 cnt1,cnt2
for (int k=1;k<=i;k++){
int L=1,R=i;
while (L<R){
int Mid=(L+R)>>1;
if (k==Mid) break;
if (k<Mid) cnt2[i][k]++,R=Mid-1;
else cnt1[i][k]++,L=Mid+1;
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=i;j++){
int cnt=0;
// i个元素中,j位于k时能够找到j的方案数
for (int k=1;k<=i;k++) cnt=(cnt+1ll*A[j-1][cnt1[i][k]]*A[i-j][cnt2[i][k]]%p*A[i-cnt1[i][k]-cnt2[i][k]-1][i-cnt1[i][k]-cnt2[i][k]-1]%p)%p;
printf("%d ",cnt);
}
puts("");
}
return 0;
}