传送门
设
d
p
[
n
]
dp[n]
dp[n]表示在
k
k
k的情况下的答案,那么考虑
n
n
n个数中最小的那个数的放置情况,显然只能放在前
k
k
k个数中,于是枚举这个数的位置,那么更新方程就是
d
p
[
n
]
=
∑
i
=
1
k
d
p
[
n
−
i
]
⋅
C
n
−
1
i
−
1
⋅
(
i
−
1
)
!
dp[n]=\sum_{i=1}^{k}dp[n-i]\cdot C_{n-1}^{i-1}\cdot (i-1)!
dp[n]=∑i=1kdp[n−i]⋅Cn−1i−1⋅(i−1)!,这个方程的更新总复杂度是
O
(
n
k
)
O(nk)
O(nk),考虑变形一下,得到
d
p
[
n
]
=
(
n
−
1
)
!
∑
i
=
1
k
d
p
[
n
−
i
]
(
n
−
i
)
!
dp[n]=(n-1)!\sum_{i=1}^k\frac {dp[n-i]}{(n-i)!}
dp[n]=(n−1)!∑i=1k(n−i)!dp[n−i],于是就是套路地前缀和优化下就行了。
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = 1e7+5;
int dp[maxn],sum[maxn],fac[maxn],fav[maxn];
int qpow(int a,int b,int c){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
b>>=1;
a=1ll*a*a%mod;
}
return ans;
}
int main(){
int n,k;
fac[0]=1;
for(int i=1;i<maxn;++i)fac[i]=1ll*fac[i-1]*i%mod;
fav[maxn-1]=qpow(fac[maxn-1],mod-2,mod);
for(int i=maxn-2;i>=0;--i)fav[i]=1ll*fav[i+1]*(i+1)%mod;
scanf("%d%d",&n,&k);
dp[0]=dp[1]=1;sum[1]=1,sum[2]=2;
for(int i=2;i<=n;++i){
dp[i]=1ll*fac[i-1]*(sum[i]-sum[max(0,i-k)])%mod;
sum[i+1]=(sum[i]+1ll*dp[i]*fav[i]%mod)%mod;
}
dp[n]=(dp[n]+mod)%mod;
printf("%d\n",dp[n]);
}