【CF886E】Maximum Element

题目

考虑正难则反,答案即为\(n!-\text{返回值为n的排列数}\)

一个排列的返回值为\(n\),当且仅当在\(n\)出现之前没有一个数后面有连续\(k\)个小于它的数

\(f_i\)表示\(1\)\(i\)的排列中,没有任何一个数后面有连续\(k\)个小于它的数

枚举\(i\)的位置,则有

\[f_i=\sum_{j=1}^kA_{i-1}^{j-1}f_{i-j} \]

即把\(i\)放在倒数第\(j\)个位置,从\(1\)\(i-1\)中选\(j-1\)个数排列在\(i\)后面,剩下\(i-j\)个个数放在\(i\)前面并且保证合法

把排列数打开,就变成了\(f_i=(i-1)!\sum_{j=1}^{k}\frac{f_{i-j}}{(i-j)!}\)

维护一下\(\frac{f_i}{i!}\)的前缀和就可以快速转移了

枚举一下\(n\)所在的位置,返回值为\(n\)的排列数即为

\[\sum_{i=1}^nf_{i-1}\binom{n-1}{i-1}(n-i)! \]

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=1e9+7;
const int maxn=1e6+5;
int pre[maxn],fac[maxn],ifac[maxn],inv[maxn],f[maxn];
int n,m;
inline int C(int n,int m) {
	if(n<m) return 0;
	return 1ll*fac[n]*ifac[n-m]%mod*ifac[m]%mod;
}
inline int qm(int x) {return x>=mod?x-mod:x;}
inline int dqm(int x) {return x<0?x+mod:x;}
inline int calc(int l,int r) {
	if(l<=0) return pre[r];
	return dqm(pre[r]-pre[l-1]);
}
int main() {
	n=read(),m=read();fac[0]=ifac[0]=inv[1]=1;
	for(re int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
	for(re int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(re int i=1;i<=n;i++) ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
	f[0]=1;pre[0]=1;
	for(re int i=1;i<n;i++) {
		f[i]=1ll*calc(i-m,i-1)*fac[i-1]%mod;
		pre[i]=qm(pre[i-1]+1ll*f[i]*ifac[i]%mod);
	}
	int ans=0;
	for(re int i=1;i<=n;i++)
		ans=qm(ans+1ll*f[i-1]*C(n-1,i-1)%mod*fac[n-i]%mod);
	printf("%d\n",dqm(fac[n]-ans));
	return 0;
}
posted @ 2019-09-27 08:57  asuldb  阅读(170)  评论(0编辑  收藏  举报