$n \leq 2000000$的排列,问有多少满足:存在个$i$,使得$p_i \neq n$,且$p_j<p_i,j \in [i+1,i+K]$,$K \leq 2000000$是给定常数。膜$1e9+7$。
排列题还是比较菜。。
这次的切入点依然是排列题的经典套路--考虑将$n$加入$n-1$的合法排列,从而建立递推关系。
先从答案要求入手,假如把$n$插进位置$i$,那么$i$之前的序列必须已经不合法,否则要么接下来一个数是$n$,后面$K$个数一定$<n$,就gg。也就是说,$n$之前的数字的大小关系已经确定了。确定大小关系的情况可以开始递推:$D(n)$表示$n$在位置$n$时,剩下$n-1$个数乱排时的不合法排列数——$n$在位置$i$时,前$i$个数一旦确定,他们的大小关系必须如同$D(n)$的方案,然后其他的数乱排列。因此最终答案为$\sum_{i=1}^{n}D(i)\frac{(n-1)!}{(i-1)!}$。搞定。
注意这里通过大小关系把$n$变成更小的东西。
现在试着求$D(n)$。首先$n<=K$时$D(n)=0$这实际上排除了一重条件$p_i \neq n$,因为此时造成$p_j<p_i,j \in [i+1,i+K]$的只有非$n$的数。好那就来看看剩下最大的$n-1$。当$n-1$放在前$n-K-1$个位置时,它就是符合条件的$i$。当它放在$n-K$往后的位置时,又来!此时$n-1$后边是不可能有非法$i$了,但前面一定有,大小关系又是$D$!于是有$D(n)=(n-K-1)(n-2)!+\sum_{i=n-K}^{n-1}D(i)*\frac{(n-2)!}{(i-1)!}$,把$(n-2)!$提到前面,记个前缀和即可。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 //#include<queue> 6 //#include<time.h> 7 //#include<complex> 8 #include<algorithm> 9 #include<stdlib.h> 10 using namespace std; 11 12 int n,K; 13 #define maxn 2000011 14 const int mod=1e9+7; 15 int fac[maxn],inv[maxn]; 16 17 int powmod(int a,int b) 18 { 19 int ans=1; 20 while (b) 21 { 22 if (b&1) ans=1ll*a*ans%mod; 23 a=1ll*a*a%mod; b>>=1; 24 } 25 return ans; 26 } 27 28 int sum[maxn],f[maxn]; 29 int main() 30 { 31 scanf("%d%d",&n,&K); 32 fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*1ll*i%mod; 33 inv[n]=powmod(fac[n],mod-2); for (int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod; 34 for (int i=1;i<=K;i++) f[i]=sum[i]=0; 35 for (int i=K+1;i<=n;i++) 36 { 37 f[i]=(1ll*(i-K-1)*fac[i-2]%mod+1ll*fac[i-2]*(sum[i-1]+mod-sum[i-K-1])%mod)%mod; 38 sum[i]=(sum[i-1]+1ll*f[i]*inv[i-1])%mod; 39 } 40 int ans=0; 41 for (int i=1;i<=n;i++) ans=(ans+1ll*(sum[i]-sum[i-1]+mod)*fac[n-1])%mod; 42 printf("%d\n",ans); 43 return 0; 44 }