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;
}
posted @ 2021-03-24 16:36  chihik  阅读(49)  评论(0编辑  收藏  举报