题意:
如果一个排列中,对于任意的 i ∈ [ k + 1 , n ] i\in[k+1,n] i∈[k+1,n],都有 a [ i ] > m i n ( a [ i − k ] , . . . , a [ i − 1 ] ) a[i]>min(a[i-k],...,a[i-1]) a[i]>min(a[i−k],...,a[i−1])成立,那么这个排列就是一个好的排列。 n n n的排列中有多少个排列是好的排列?
Solution:
要这样的排列存在,首先必须让
1
1
1出现在区间
[
1
,
k
]
[1,k]
[1,k]内,设
d
p
[
i
]
dp[i]
dp[i]为用
i
i
i个不相同的数计算出的这样的排列有多少个,不妨枚举
1
1
1出现的位置
j
j
j,那么
[
1
,
j
−
1
]
[1,j-1]
[1,j−1]位置任意放置任何数,
[
j
+
1
,
i
]
[j+1,i]
[j+1,i]用另外的
i
−
j
i-j
i−j个数重新构成一个这样的排列,于是
d
p
[
i
]
=
∑
j
=
1
k
A
i
−
1
j
−
1
d
p
[
i
−
j
]
dp[i]=\sum_{j=1}^{k}A_{i-1}^{j-1}dp[i-j]
dp[i]=j=1∑kAi−1j−1dp[i−j]
这样的单次转移是
O
(
k
)
O(k)
O(k)的,但可以写成这样
d
p
[
i
]
=
(
i
−
1
)
!
∑
j
=
1
k
d
p
[
i
−
j
]
(
i
−
j
)
!
dp[i]=(i-1)!\sum_{j=1}^{k}\frac{dp[i-j]}{(i-j)!}
dp[i]=(i−1)!j=1∑k(i−j)!dp[i−j]
显然求和是区间和形式,维护
d
p
[
i
]
i
!
\frac{dp[i]}{i!}
i!dp[i]的前缀和就可以
O
(
1
)
O(1)
O(1)转移
全排列问题似乎不需要太注意全排列?用 t t t组每组元素都互异的数来等价替换全排列
// #include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<queue>
#include<complex>
#include<cstdio>
#include<bitset>
#include<stack>
#include<cmath>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
using ll=long long;
const int N=1e7+5,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;
ll dp[N],fac[N],invfac[N],sum[N];
int n,k;
ll qpow(ll a,ll b)
{
ll ret=1,base=a;
while(b)
{
if(b&1) ret=ret*base%mod;
base=base*base%mod;
b>>=1;
}
return ret;
}
void initial()
{
for(int i=fac[0]=invfac[0]=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
invfac[N-1]=qpow(fac[N-1],mod-2);
for(int i=N-2;i>=1;i--) invfac[i]=invfac[i+1]*(i+1)%mod;
}
int main()
{
initial();
cin>>n>>k;
//有多少个n的排列,使得a[i]>min(a[i-k],a[i-k+1],...,a[i-1]);
for(int i=1;i<=k;i++)
{
dp[i]=fac[i];
sum[i]=(sum[i-1]+dp[i]*invfac[i]%mod)%mod;
}
for(int i=k+1;i<=n;i++)
{
dp[i]=fac[i-1]*(((sum[i-1]-sum[i-k-1])%mod+mod)%mod)%mod;
sum[i]=(sum[i-1]+dp[i]*invfac[i]%mod)%mod;
}
cout<<dp[n];
return 0;
}