CF886E Maximum Element
我们只关心元素的大小关系,并且是排列计数(即元素不同),所以任意一个子序列都可看作一个排列。
令 \(f_i\) 表示 \(1 \sim i\) 的所有排列,没有中途退出的排列数。(这个返回值应该是 \(i\) )
显然满足要求的排列的最大值 \(i\) 的位置只能在 \([i-k+1,i]\) , 不妨枚举这个位置
那么有:
\[\begin{aligned}
f_i&=\sum_{j=i-k+1}^i \binom{i-1}{j-1}f_{j-1} (i-j)! \\
&=\sum_{j=i-k+1}^i f_{j-1} \frac{(i-1)!}{(j-1)!} \\
&=(i-1)!\sum_{j=i-k}^{i-1} \frac{f_j}{j!}
\end{aligned}\]
维护长度为 \(k\) 的 \(\frac{f_i}{i!}\) 的值的和,便可以 \(\mathcal O(n)\) 推出 \(f\)。
现在再考虑如何计算答案,显然我们可以用总排列数减去返回值为 \(n\) 的排列数。
同样我们可以枚举 \(n\) 的位置,那么我们只需要保证 \(n\) 之前没有返回即可,答案即为
\[\sum_{i=1}^n \binom{n-1}{i-1}f_{i-1}(n-i)!
\]
最后用 \(n!\) 减去上面的答案即可。
#include <cstdio>
const int MAXN = 1e6 , Mod = 1e9 + 7;
int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int Qkpow( int x , int po ) { int Ans = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) Ans = Mul( Ans , x ); return Ans; }
int Inv( int x ) { return Qkpow( x , Mod - 2 ); }
int fac[ MAXN + 5 ] , ivf[ MAXN + 5 ];
void Init() {
fac[ 0 ] = 1;
for( int i = 1 ; i <= MAXN ; i ++ ) fac[ i ] = Mul( fac[ i - 1 ] , i );
ivf[ MAXN ] = Inv( fac[ MAXN ] );
for( int i = MAXN ; i >= 1 ; i -- ) ivf[ i - 1 ] = Mul( ivf[ i ] , i );
}
int C( int n , int m ) { return n < m ? 0 : Mul( fac[ n ] , Mul( ivf[ m ] , ivf[ n - m ] ) ); }
int n , k , f[ MAXN + 5 ] , s;
int main( ) {
Init();
scanf("%d %d",&n,&k);
f[ 0 ] = 1; s = 1;
for( int i = 1 ; i <= n ; i ++ ) {
f[ i ] = Mul( fac[ i - 1 ] , s );
s = Add( s , Mul( f[ i ] , ivf[ i ] ) );
if( i - k >= 0 ) s = Sub( s , Mul( f[ i - k ] , ivf[ i - k ] ) );
}
int Ans = fac[ n ];
for( int i = 1 ; i <= n ; i ++ ) Ans = Sub( Ans , Mul( C( n - 1 , i - 1 ) , Mul( f[ i - 1 ] , fac[ n - i ] ) ) );
printf("%d\n", Ans );
return 0;
}