Codeforces889C. Maximum Element

$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(i)$表示$i$在位置$i$时,剩下$i-1$个数乱排时的合法排列数——$n$(注意,这里真的是$n$)在位置$i$时,前$i-1$个数一旦确定,他们的大小关系必须如同$D(i)$的方案,然后其他的数乱排列。因此最终答案为$\sum_{i=1}^{n}D(i)\frac{(n-1)!}{(i-1)!}$。搞定。

注意这里通过大小关系把$n$变成更小的东西。

现在试着求$D(i)$。首先$i<=K$时$D(i)=0$这实际上排除了一重条件$p_i \neq n$,因为此时造成$p_j<p_i,j \in [i+1,i+K]$的只有非$n$的数。好那就来看看剩下最大的$n-1$。当$n-1$放在前$i-K-1$个位置时,它就是符合条件的$i$。当它放在$i-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 }
View Code
posted @ 2018-03-15 21:17  Blue233333  阅读(256)  评论(0编辑  收藏  举报