题目:
给定
n
n
n和
k
k
k,问有多少个
[
1
,
n
]
[1,n]
[1,n]的排列
a
a
a满足对于
∀
i
>
k
,
a
i
>
min
1
≤
j
≤
k
{
a
i
−
j
}
\forall i>k,a_i>\min \limits_{1 \le j \le k}\{a_{i-j}\}
∀i>k,ai>1≤j≤kmin{ai−j},答案模998244353。
(
1
≤
n
,
k
≤
1
0
7
)
(1 \le n,k \le 10^7)
(1≤n,k≤107)
题解:
首先可以发现1一定在
[
1
,
k
]
[1,k]
[1,k]的位置中,假设为
x
x
x,那么可以在
[
1
,
x
−
1
]
[1,x-1]
[1,x−1]的位置上安排任意的数,然后对于最小的没有安排在
[
1
,
x
]
[1,x]
[1,x]范围内的数,设为
y
y
y,这个数一定要安排在
[
x
+
1
,
x
+
k
]
[x+1,x+k]
[x+1,x+k]中的一个位置上。那么就可以发现安排完1的位置
x
x
x后,再将
[
1
,
x
−
1
]
[1,x-1]
[1,x−1]的位置填满,再将剩下的没安排的数离散化一下,问题就变成了一个原问题的子问题了,所以可以用
d
p
dp
dp来搞。
令
d
p
i
dp_i
dpi为给定
i
,
k
i,k
i,k的问题的答案,那么可以通过枚举
1
1
1的位置来进行转移,即
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}
dpi=j=1∑kAi−1j−1dpi−j这个式子可以继续优化,
d
p
i
=
∑
j
=
1
k
(
i
−
1
)
!
(
i
−
j
)
!
d
p
i
−
j
dp_i=\sum_{j=1}^k \frac{(i-1)!}{(i-j)!}dp_{i-j}
dpi=j=1∑k(i−j)!(i−1)!dpi−j
维护前缀和
∑
j
=
1
i
1
j
!
d
p
j
\displaystyle \sum_{j=1}^i\frac{1}{j!}dp_j
j=1∑ij!1dpj即可线性递推。
复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=998244353;
const int INF=0x3f3f3f3f;
const int maxn=1e7+5;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k;
ll dp[maxn],pre[maxn],inv[maxn],p[maxn];
int que[maxn];
ll qpow(ll a,ll p=mod-2){
ll res=1;
while(p){
if(p&1)res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res;
}
void init(int n){
pre[0]=1;
for(int i=1;i<=n;i++)pre[i]=pre[i-1]*i%mod;
inv[n]=qpow(pre[n]);
for(int i=n-1;i>=0;i--){
inv[i]=inv[i+1]*(i+1)%mod;
}
}
int main(void){
// freopen("in.txt","r",stdin);
init(10000000);
scanf("%d%d",&n,&k);
p[0]=0;
for(int i=1;i<=k;i++){
dp[i]=pre[i];
p[i]=(p[i-1]+dp[i]*inv[i]%mod)%mod;
}
for(int i=k+1;i<=n;i++){
dp[i]=(p[i-1]-p[i-k-1])%mod*pre[i-1]%mod;
p[i]=(p[i-1]+dp[i]*inv[i]%mod)%mod;
}
printf("%lld\n",(dp[n]+mod)%mod);
return 0;
}