Solution\text{Solution}Solution
纯纯的dp题。
关键在于我们 dp 时只关注不同元素之间的相对大小。
非法情况不易统计,考虑转而考虑合法情况再用全排列减。
设计 fif_ifi 为长度为 iii 的排列循环到一直最后也没有跳出的方案数。
枚举最大的元素 iii 放置的位置 jjj,由于不能跳出,jjj 只能放在[i−k+1,i][i-k+1,i][i−k+1,i] 的位置。
那么就有:
fi=∑j=i−k+1i(i−1j−1)×fj−1×(j−i)!f_i=\sum_{j=i-k+1}^i \binom{i-1}{j-1}\times f_{j-1}\times (j-i)!fi=j=i−k+1∑i(j−1i−1)×fj−1×(j−i)!
解释一下,当元素 iii 放在位置 jjj 时,选出 j−1j-1j−1 个数放前面,方案是(i−1j−1)\binom{i-1}{j-1}(j−1i−1);前面的方案就是 fj−1f_{j-1}fj−1(和排列是等价的),后面随便放,方案就是阶乘。
然后把组合数拆一下,变成:
fi=∑j=i−k+1i(i−1)!(j−1)!×fj−1f_i=\sum_{j=i-k+1}^i\frac{(i-1)!}{(j-1)!} \times f_{j-1}fi=j=i−k+1∑i(j−1)!(i−1)!×fj−1
=(i−1)!×∑j=i−k+1ifj−1(j−1)!=(i-1)!\times\sum_{j=i-k+1}^i\frac{f_{j-1}}{(j-1)!} =(i−1)!×j=i−k+1∑i(j−1)!fj−1
=(i−1)!×∑j=i−ki−1fjj!=(i-1)!\times\sum_{j=i-k}^{i-1}\frac{f_{j}}{j!} =(i−1)!×j=i−k∑i−1j!fj
把后面的东西拿前缀和优化一下即可线性求出 fff 数组。
求出 fff 之后,枚举 nnn 所在的位置 iii,答案就是:
ans=∑i=1n(n−1i−1)×fi−1×(n−i)!ans=\sum_{i=1}^n\binom{n-1}{i-1}\times f_{i-1}\times (n-i)!ans=i=1∑n(i−1n−1)×fi−1×(n−i)!
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}const int N=1e6+100;
const int mod=1e9+7;
int n,m,k;
inline ll ksm(ll x,ll k){ll res(1);while(k){if(k&1) res=x*res%mod;x=x*x%mod;k>>=1;}return res;
}
ll jc[N],ni[N];
ll f[N],sum[N];
inline ll C(ll n,ll m){return jc[n]*ni[m]%mod*ni[n-m]%mod;
}
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();k=read();jc[0]=1;for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;ni[n]=ksm(jc[n],mod-2);for(int i=n-1;i>=0;i--) ni[i]=ni[i+1]*(i+1)%mod;f[0]=1;sum[0]=1;for(int i=1;i<=n;i++){f[i]=jc[i-1]*(sum[i-1]+mod-(i-k>0?sum[i-k-1]:0))%mod;sum[i]=(sum[i-1]+f[i]*ni[i])%mod;}ll ans(0);for(int i=1;i<=n;i++){(ans+=f[i-1]*C(n-1,i-1)%mod*jc[n-i]%mod)%=mod;}printf("%lld\n",(jc[n]+mod-ans)%mod);return 0;
}
/*
*/