题目描述
定义一个正整数序列 p \texttt{p} p,称其是合法的当且仅当对于所有在 p \texttt{p} p中出现过且 > 1 > 1 >1的正整数 k k k,存在 i < j i<j i<j,满足 p i = k − 1 , p j = k p_i=k-1,p_j=k pi=k−1,pj=k。
定义 f ( k ) f(k) f(k)为 k k k在所有长度为 n n n的好序列中的出现次数之和。
∀ k ∈ [ 1 , n ] \forall k\in[1,n] ∀k∈[1,n]求 f ( k ) f(k) f(k)。
n ≤ 5000 n\leq 5000 n≤5000
解题思路
非常妙的映射!
可证明每个合法序列和一个排列一一对应:
- 1.考虑怎么把排列对应成一个合法序列:
考虑把排列划分成若干极长的连续下降段,若第 i i i段里有数 x x x,就让 p x = i p_x=i px=i。
这样构造的 p p p一定是合法的,否则就说明存在一个 k k k,使得所有 k k k的出现位置都在 k − 1 k-1 k−1的出现位置前面,那么这两个连续段应该会变成一个,就矛盾了。
- 2.一个合法序列对应一个排列
这是显然的,把上述过程逆过来就好了。
因此这是一个双射。
那么对于一个位置 i i i,它对 f ( k ) f(k) f(k)的贡献就是它被划分在第 k k k段的排列数。
对于位置 i i i,若 p i < p i + 1 p_i<p_{i+1} pi<pi+1,我们称之为一个上升。
则 i i i被划分在第 k k k段的排列数等价与前 i i i个位置恰好有 k − 1 k-1 k−1个上升。
设长度为
n
n
n且有
m
m
m个上升的排列个数为
d
p
n
,
m
dp_{n,m}
dpn,m,则
f
(
k
)
=
∑
i
=
1
n
d
p
i
,
k
−
1
C
n
i
(
n
−
i
)
!
f(k)=\sum_{i=1}^ndp_{i,k-1}C_n^i(n-i)!
f(k)=i=1∑ndpi,k−1Cni(n−i)!
d p n , m dp_{n,m} dpn,m的递推是好求的,考虑加入最小的数:
-
加入后上升个数不变,有 m + 1 m+1 m+1种选择,因为可以填到末尾
-
加入后上升个数加一,有 n − m n-m n−m种选择,因为可以填到开头
总复杂度 O ( n 2 ) O(n^2) O(n2).
#include<bits/stdc++.h>
using namespace std;
const int N = 5050;
int dp[N][N];
int n;
const int mod = 998244353;
inline int myplus(int a,int b){return (a+b>=mod?a+b-mod:a+b);}
int ans[N],C[N][N],fac[N];
int main()
{
cin>>n;
dp[1][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i-1;j++)
{
dp[i+1][j]=myplus(dp[i+1][j],1ll*dp[i][j]*(j+1)%mod);
dp[i+1][j+1]=myplus(dp[i+1][j+1],1ll*dp[i][j]*(i-j)%mod);
}
C[0][0]=1;fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=1ll*fac[i-1]*i%mod;
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=myplus(C[i-1][j-1],C[i-1][j]);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
ans[k]=myplus(ans[k],1ll*dp[i][k-1]*C[n][i]%mod*fac[n-i]%mod);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}